diff --git a/packages/core/src/dom_components/model/Component.ts b/packages/core/src/dom_components/model/Component.ts index 1dae41ba4..aaf0b90e1 100644 --- a/packages/core/src/dom_components/model/Component.ts +++ b/packages/core/src/dom_components/model/Component.ts @@ -11,7 +11,15 @@ import { bindAll, keys, } from 'underscore'; -import { shallowDiff, capitalize, isEmptyObj, isObject, toLowerCase } from '../../utils/mixins'; +import { + shallowDiff, + capitalize, + isEmptyObj, + isObject, + toLowerCase, + escapeAltQuoteAttrValue, + escapeAttrValue, +} from '../../utils/mixins'; import StyleableModel, { GetStyleOpts, StyleProps, @@ -1597,9 +1605,9 @@ export default class Component extends StyleableModel { } else { let valueRes = ''; if (opts.altQuoteAttr && isString(val) && val.indexOf('"') >= 0) { - valueRes = `'${val.replace(/'/g, ''')}'`; + valueRes = `'${escapeAltQuoteAttrValue(val)}'`; } else { - const value = isString(val) ? val.replace(/"/g, '"') : val; + const value = isString(val) ? escapeAttrValue(val) : val; valueRes = `"${value}"`; } diff --git a/packages/core/src/utils/mixins.ts b/packages/core/src/utils/mixins.ts index 038461210..8426b796d 100644 --- a/packages/core/src/utils/mixins.ts +++ b/packages/core/src/utils/mixins.ts @@ -192,6 +192,14 @@ export const escapeNodeContent = (str = '') => { return `${str}`.replace(/&/g, '&').replace(//g, '>'); }; +export const escapeAttrValue = (str = '') => { + return `${str}`.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +}; + +export const escapeAltQuoteAttrValue = (str = '') => { + return `${str}`.replace(/&/g, '&').replace(//g, '>').replace(/'/g, '''); +}; + export const deepMerge = (...args: ObjectAny[]) => { const target = { ...args[0] }; diff --git a/packages/core/test/specs/dom_components/model/Component.ts b/packages/core/test/specs/dom_components/model/Component.ts index 6069c05af..bbe957416 100644 --- a/packages/core/test/specs/dom_components/model/Component.ts +++ b/packages/core/test/specs/dom_components/model/Component.ts @@ -155,7 +155,7 @@ describe('Component', () => { obj.set({ bool: true, removable: false, - string: 'st\'ri"ng', + string: 'st\'ri"ng&<>', array: [1, 'string', true], object: { a: 1, b: 'string', c: true }, null: null, @@ -164,12 +164,12 @@ describe('Component', () => { zero: 0, _private: 'value', }); - let resStr = "st'ri"ng"; + let resStr = "st'ri"ng&<>"; let resArr = '[1,"string",true]'; let resObj = '{"a":1,"b":"string","c":true}'; let res = `
`; expect(obj.toHTML({ withProps: true })).toEqual(res); - resStr = 'st'ri"ng'; + resStr = 'st'ri"ng&<>'; resArr = '[1,"string",true]'; resObj = '{"a":1,"b":"string","c":true}'; res = `
`;