From ce7eaf56d30080ef4e6312c82f2d03c65cbe0253 Mon Sep 17 00:00:00 2001 From: pimlie Date: Fri, 8 Mar 2019 18:53:13 +0100 Subject: [PATCH] test: add missing test for sanitizeByTagId fix: broken sanitizeByTagId implementation --- src/shared/escape.js | 21 ++++++++++++--------- src/shared/getMetaInfo.js | 19 +++++++++++++------ test/escaping.test.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/shared/escape.js b/src/shared/escape.js index d645a04..77b73e3 100644 --- a/src/shared/escape.js +++ b/src/shared/escape.js @@ -3,7 +3,9 @@ import isArray from './isArray' import { isString, isObject } from './typeof' // sanitizes potentially dangerous characters -export default function escape(info, { tagIDKeyName }, escapeSequences = []) { +export default function escape(info, options, escapeOptions) { + const { tagIDKeyName } = options + const { doEscape = v => v } = escapeOptions const escaped = {} for (const key in info) { @@ -16,32 +18,33 @@ export default function escape(info, { tagIDKeyName }, escapeSequences = []) { } let [ disableKey ] = disableOptionKeys - if (info[disableKey] && info[disableKey].includes(key)) { + if (escapeOptions[disableKey] && escapeOptions[disableKey].includes(key)) { // this info[key] doesnt need to escaped if the option is listed in __dangerouslyDisableSanitizers escaped[key] = value continue } - if (info[tagIDKeyName]) { + const tagId = info[tagIDKeyName] + if (tagId) { disableKey = disableOptionKeys[1] - // items which vmid is listed in __dangerouslyDisableSanitizersByTagID do not need to be escaped - if (info[disableKey] && info[disableKey][key] && info[disableKey][key].includes(info[tagIDKeyName])) { + // keys which are listed in __dangerouslyDisableSanitizersByTagID for the current vmid do not need to be escaped + if (escapeOptions[disableKey] && escapeOptions[disableKey][tagId] && escapeOptions[disableKey][tagId].includes(key)) { escaped[key] = value continue } } if (isString(value)) { - escaped[key] = escapeSequences.reduce((val, [v, r]) => val.replace(v, r), value) + escaped[key] = doEscape(value) } else if (isArray(value)) { escaped[key] = value.map((v) => { return isObject(v) - ? escape(v, { tagIDKeyName }, escapeSequences) - : escapeSequences.reduce((val, [v, r]) => val.replace(v, r), v) + ? escape(v, options, escapeOptions) + : doEscape(v) }) } else if (isObject(value)) { - escaped[key] = escape(value, { tagIDKeyName }, escapeSequences) + escaped[key] = escape(value, options, escapeOptions) } else { escaped[key] = value } diff --git a/src/shared/getMetaInfo.js b/src/shared/getMetaInfo.js index f15a618..1106b6c 100644 --- a/src/shared/getMetaInfo.js +++ b/src/shared/getMetaInfo.js @@ -33,21 +33,28 @@ export default function getMetaInfo(options = {}, component, escapeSequences = [ info.base = Object.keys(info.base).length ? [info.base] : [] } - for (const index in disableOptionKeys) { - const disableKey = disableOptionKeys[index] + const escapeOptions = { + doEscape: value => escapeSequences.reduce((val, [v, r]) => val.replace(v, r), value) + } + + disableOptionKeys.forEach((disableKey, index) => { if (!info[disableKey]) { - continue + return } if (index === 0) { ensureIsArray(info, disableKey) } else if (index === 1) { - info[disableKey].forEach(key => ensureIsArray(info[disableKey], key)) + for (const key in info[disableKey]) { + ensureIsArray(info[disableKey], key) + } } - } + + escapeOptions[disableKey] = info[disableKey] + }) // begin sanitization - info = escape(info, options, escapeSequences) + info = escape(info, options, escapeOptions) return info } diff --git a/test/escaping.test.js b/test/escaping.test.js index 6ccb00f..1d5afa8 100644 --- a/test/escaping.test.js +++ b/test/escaping.test.js @@ -34,4 +34,37 @@ describe('escaping', () => { __dangerouslyDisableSanitizersByTagID: {} }) }) + + test('special chars are escaped unless disabled by vmid', () => { + const component = new Vue({ + metaInfo: { + title: 'Hello', + script: [ + { vmid: 'yescape', innerHTML: 'Hello & Goodbye' }, + { vmid: 'noscape', innerHTML: 'Hello & Goodbye' } + ], + __dangerouslyDisableSanitizersByTagID: { noscape: ['innerHTML'] } + } + }) + + expect(getMetaInfo(component, [[/&/g, '&']])).toEqual({ + title: 'Hello', + titleChunk: 'Hello', + titleTemplate: '%s', + htmlAttrs: {}, + headAttrs: {}, + bodyAttrs: {}, + meta: [], + base: [], + link: [], + style: [], + script: [ + { innerHTML: 'Hello & Goodbye', vmid: 'yescape' }, + { innerHTML: 'Hello & Goodbye', vmid: 'noscape' } + ], + noscript: [], + __dangerouslyDisableSanitizers: [], + __dangerouslyDisableSanitizersByTagID: { noscape: ['innerHTML'] } + }) + }) })