From 38ef0d99bc9ab768af46e3533b5ac34381f0edee Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 1 Apr 2017 14:07:05 +0800 Subject: [PATCH] fix ssr xss (fix #5351) --- src/core/vdom/helpers/resolve-async-component.js | 7 +++++++ src/platforms/web/server/modules/attrs.js | 4 +++- src/platforms/web/server/modules/class.js | 3 ++- src/platforms/web/server/modules/style.js | 4 +++- test/ssr/ssr-string.spec.js | 15 +++++++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/core/vdom/helpers/resolve-async-component.js b/src/core/vdom/helpers/resolve-async-component.js index a8cf9ace182..241e332f8ac 100644 --- a/src/core/vdom/helpers/resolve-async-component.js +++ b/src/core/vdom/helpers/resolve-async-component.js @@ -1,5 +1,12 @@ /* @flow */ +// () => ({ +// component: import('./xxx.vue'), +// delay: 200, +// loading: LoadingComponent, +// error: ErrorComponent +// }) + import { warn, isObject diff --git a/src/platforms/web/server/modules/attrs.js b/src/platforms/web/server/modules/attrs.js index 6a615763333..922dc384990 100644 --- a/src/platforms/web/server/modules/attrs.js +++ b/src/platforms/web/server/modules/attrs.js @@ -1,5 +1,7 @@ /* @flow */ +import { escape } from 'he' + import { isBooleanAttr, isEnumeratedAttr, @@ -40,7 +42,7 @@ export function renderAttr (key: string, value: string): string { } else if (isEnumeratedAttr(key)) { return ` ${key}="${isFalsyAttrValue(value) || value === 'false' ? 'false' : 'true'}"` } else if (!isFalsyAttrValue(value)) { - return ` ${key}="${value}"` + return ` ${key}="${typeof value === 'string' ? escape(value) : value}"` } return '' } diff --git a/src/platforms/web/server/modules/class.js b/src/platforms/web/server/modules/class.js index 69a84d7bdf7..c972c24dba6 100644 --- a/src/platforms/web/server/modules/class.js +++ b/src/platforms/web/server/modules/class.js @@ -1,10 +1,11 @@ /* @flow */ +import { escape } from 'he' import { genClassForVnode } from 'web/util/index' export default function renderClass (node: VNodeWithData): ?string { const classList = genClassForVnode(node) if (classList) { - return ` class="${classList}"` + return ` class="${escape(classList)}"` } } diff --git a/src/platforms/web/server/modules/style.js b/src/platforms/web/server/modules/style.js index 8c66ed01452..bed5318cdb4 100644 --- a/src/platforms/web/server/modules/style.js +++ b/src/platforms/web/server/modules/style.js @@ -1,4 +1,6 @@ /* @flow */ + +import { escape } from 'he' import { hyphenate } from 'shared/util' import { getStyle } from 'web/util/style' @@ -14,6 +16,6 @@ function genStyleText (vnode: VNode): string { export default function renderStyle (vnode: VNodeWithData): ?string { const styleText = genStyleText(vnode) if (styleText) { - return ` style=${JSON.stringify(styleText)}` + return ` style=${JSON.stringify(escape(styleText))}` } } diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js index bfb05c91cc7..86b25e219c8 100644 --- a/test/ssr/ssr-string.spec.js +++ b/test/ssr/ssr-string.spec.js @@ -761,6 +761,21 @@ describe('SSR: renderToString', () => { }) expect(vm.a).toBe(func) }) + + it('should prevent xss in attribtues', () => { + renderVmWithOptions({ + data: { + xss: '">' + }, + template: ` +
+ foo +
+ ` + }, res => { + expect(res).not.toContain(``) + }) + }) }) function renderVmWithOptions (options, cb) {