From 03cb80a980fb109a8512a7d7e99325dd39a4fc5c Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 10 Aug 2020 19:33:50 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B3=A8=E5=85=A5HTML=E5=88=B0document?= =?UTF-8?q?=E5=89=8D=EF=BC=8C=E9=BB=98=E8=AE=A4=E5=AF=B9=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E6=88=90=E7=9A=84HTML=E8=BF=9B=E8=A1=8C=E9=98=B2XSS=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mavon-editor.vue | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/mavon-editor.vue b/src/mavon-editor.vue index f44b2a76d..4428a2e85 100644 --- a/src/mavon-editor.vue +++ b/src/mavon-editor.vue @@ -197,7 +197,7 @@ export default { return CONFIG.toolbars } }, - xssOptions: { // 工具栏 + xssOptions: { // XSS 选项 type: Object, default() { return null @@ -644,6 +644,10 @@ export default { var $vm = this; this.$render($vm.d_value, function(res) { // render + + // HTML 渲染前先进行过滤,避免 xss 问题,默认情况下开始此功能 + res = xss(res, $vm.$props.xssOptions || {}) + $vm.d_render = res; // change回调 toggleChange == false 时候触发change回调 if (!toggleChange) @@ -674,14 +678,6 @@ export default { this.iRender(); }, value: function (val, oldVal) { - // Escaping all XSS characters - // escapeHtml (html) { - // return html - // } - if (this.xssOptions) { - val = xss(val, this.xssOptions); - } - if (val !== this.d_value) { this.d_value = val } From ab3f7f9c08d7c2bcbec507a43738ee4bc4b7be70 Mon Sep 17 00:00:00 2001 From: ygj6 Date: Mon, 22 Nov 2021 23:10:39 +0800 Subject: [PATCH 2/3] feat: Enable XSS defense by default 1.Optimize code 2.refresh readme 3.fix toc anchor jump --- README-EN.md | 2 +- README.md | 2 +- src/lib/core/rules.js | 19 +++++++++++++++++++ src/lib/mixins/markdown.js | 5 +++++ src/mavon-editor.vue | 38 +++++++++++++++++++++++++++++++------- webpack/webpack.dev.js | 2 +- 6 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 src/lib/core/rules.js diff --git a/README-EN.md b/README-EN.md index 68ef5c473..5515e8fe0 100644 --- a/README-EN.md +++ b/README-EN.md @@ -122,7 +122,7 @@ export default { | imageFilter | Function | null | Image file filter Function, params is a `File Object`, you should return `Boolean` about the test result | | imageClick | function | null | Image Click Function | | tabSize | Number | null | How many spaces equals one tab, default \t | -| xssOptions | Object | null | xss options: [https://github.com/leizongmin/js-xss](https://github.com/leizongmin/js-xss) | +| xssOptions | Object | {} | xss rule configuration, enabled by default, set to false to turn off, custom rule reference [https://jsxss.com/zh/options.html](https://jsxss.com/zh/options.html) | | toolbars | Object | As in the following example | toolbars | ```javascript diff --git a/README.md b/README.md index 66f30b982..b9547955c 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ export default { | imageFilter | function | null | 图片过滤函数,参数为一个`File Object`,要求返回一个`Boolean`, `true`表示文件合法,`false`表示文件不合法 | | imageClick | function | null | 图片点击事件,默认为预览,可覆盖 | | tabSize | Number | \t | tab转化为几个空格,默认为\t | -| xssOptions | Object | null | xss规则配置,参考 [https://github.com/leizongmin/js-xss](https://github.com/leizongmin/js-xss) | +| xssOptions | Object | {} | xss规则配置, 默认开启,设置false可以关闭,自定义规则参考 [https://jsxss.com/zh/options.html](https://jsxss.com/zh/options.html) | | toolbars | Object | 如下例 | 工具栏 | ```javascript diff --git a/src/lib/core/rules.js b/src/lib/core/rules.js new file mode 100644 index 000000000..064756e16 --- /dev/null +++ b/src/lib/core/rules.js @@ -0,0 +1,19 @@ +export const HEADER_FLAG = ' _MD-HEADER_ '; + +export function headRule(tocHeadRule) { + return function (tokens, index) { + let code = tocHeadRule(tokens, index); + var label = tokens[index + 1]; + if (label.type === 'inline') { + return code.replace('). @@ -89,6 +91,9 @@ markdown.use(mihe, hljs_opts) .use(taskLists) .use(toc) +const tocHeadRule = markdown.renderer.rules.heading_open; +markdown.renderer.rules.heading_open = headRule(tocHeadRule); + export default { data() { return { diff --git a/src/mavon-editor.vue b/src/mavon-editor.vue index 4428a2e85..703b6e358 100644 --- a/src/mavon-editor.vue +++ b/src/mavon-editor.vue @@ -119,7 +119,9 @@ import md_toolbar_left from './components/md-toolbar-left.vue' import md_toolbar_right from './components/md-toolbar-right.vue' import "./lib/font/css/fontello.css" import './lib/css/md.css' -const xss = require('xss'); +import { recoverHead } from './lib/core/rules.js' +import { FilterXSS } from 'xss'; + export default { mixins: [markdown], props: { @@ -198,9 +200,9 @@ export default { } }, xssOptions: { // XSS 选项 - type: Object, + type: [Object, Boolean], default() { - return null + return {} } }, codeStyle: { // 样式 @@ -305,7 +307,8 @@ export default { }, p_external_link: {}, textarea_selectionEnd: 0, - textarea_selectionEnds: [0] + textarea_selectionEnds: [0], + _xssHandler: null }; }, created() { @@ -640,13 +643,34 @@ export default { console.warn('hljs color scheme', val, 'do not exist, hljs color scheme will not change'); } }, + xssHandler(htmlCode) { + if (this._xssHandler) { + return this._xssHandler.process(htmlCode); + } + + let originalTagFun; + if (typeof this.xssOptions['onTag'] === 'function') { + originalTagFun = this.xssOptions['onTag']; + } + this.xssOptions['onTag'] = function(tag, html, info) { + let code = recoverHead(tag, html); + if (originalTagFun) { + code = originalTagFun(tag,code); + } + if (html !== code) { + return code; + } + } + this._xssHandler = new FilterXSS(this.xssOptions); + return this._xssHandler.process(htmlCode); + }, iRender(toggleChange) { var $vm = this; this.$render($vm.d_value, function(res) { - // render - // HTML 渲染前先进行过滤,避免 xss 问题,默认情况下开始此功能 - res = xss(res, $vm.$props.xssOptions || {}) + if (typeof $vm.xssOptions === 'object') { + res = $vm.xssHandler(res); + } $vm.d_render = res; // change回调 toggleChange == false 时候触发change回调 diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js index 448cb3e50..286ebddae 100644 --- a/webpack/webpack.dev.js +++ b/webpack/webpack.dev.js @@ -41,7 +41,7 @@ var config = { // hot: true, // noInfo: true }, - devtool: '#eval-source-map' + devtool: 'source-map' } var res = merge([base, config]); From 77e2ce6601641ed75696c9b647af682f7c072b85 Mon Sep 17 00:00:00 2001 From: ygj6 Date: Tue, 23 Nov 2021 15:45:03 +0800 Subject: [PATCH 3/3] Fix code style function and text alignment function --- src/mavon-editor.vue | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/mavon-editor.vue b/src/mavon-editor.vue index 703b6e358..033fa9a74 100644 --- a/src/mavon-editor.vue +++ b/src/mavon-editor.vue @@ -661,6 +661,37 @@ export default { return code; } } + + let originalTagAttr; + if (typeof this.xssOptions['onTagAttr'] === 'function') { + originalTagAttr = this.xssOptions['onTagAttr']; + } + this.xssOptions['onTagAttr'] = function (tag, name, value) { + const whiteClass = { + "div": ['hljs-left', 'hljs-center', 'hljs-right', 'hljs-*'], + "code": ['lang-language','lang-*'], + "span": ['hljs-*'] + }; + + let newValue, oriValue; + if (name === 'class' && + whiteClass[tag] && + whiteClass[tag].find(el => { + return !!value.match(el) + })) + { + newValue = name + '="' + value + '"'; + } + + if (originalTagAttr) { + oriValue = originalTagAttr(tag, name, value); + } + + if (newValue || oriValue) { + return oriValue || newValue; + } + }; + this._xssHandler = new FilterXSS(this.xssOptions); return this._xssHandler.process(htmlCode); }, @@ -671,7 +702,6 @@ export default { if (typeof $vm.xssOptions === 'object') { res = $vm.xssHandler(res); } - $vm.d_render = res; // change回调 toggleChange == false 时候触发change回调 if (!toggleChange)