diff --git a/src/interpolate.js b/src/interpolate.js index 15b9ca4..910fb53 100644 --- a/src/interpolate.js +++ b/src/interpolate.js @@ -44,6 +44,14 @@ let interpolate = function (msgid, context = {}) { const expression = token.trim() let evaluated + let escapeHtmlMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + } + // Avoid eval() by splitting `expression` and looping through its different properties if any, see #55. function getProps (obj, expression) { const arr = expression.split(EVALUATION_RE).filter(x => x) @@ -69,6 +77,9 @@ let interpolate = function (msgid, context = {}) { } } return evaluated + .toString() + // Escape HTML, see #78. + .replace(/[&<>"']/g, function (m) { return escapeHtmlMap[m] }) } return evalInContext.call(context, expression) diff --git a/test/specs/interpolate.spec.js b/test/specs/interpolate.spec.js index 2f3647f..5849f52 100644 --- a/test/specs/interpolate.spec.js +++ b/test/specs/interpolate.spec.js @@ -29,6 +29,13 @@ describe('Interpolate tests', () => { expect(interpolated).to.equal('Foo bar baz') }) + it('with HTML in var (should be escaped)', () => { + let msgid = 'Foo %{ placeholder } baz' + let context = { placeholder: '
bar
' } + let interpolated = interpolate(msgid, context) + expect(interpolated).to.equal('Foo <p>bar</p> baz') + }) + it('with multiple spaces in the placeholder', () => { let msgid = 'Foo %{ placeholder } baz' let context = { placeholder: 'bar' }