diff --git a/app/message-attachments/client/renderField.js b/app/message-attachments/client/renderField.js index 3ba28e8ffc22..7993297fb808 100644 --- a/app/message-attachments/client/renderField.js +++ b/app/message-attachments/client/renderField.js @@ -1,6 +1,8 @@ import { Template } from 'meteor/templating'; import { Blaze } from 'meteor/blaze'; +import { escapeHTML } from '../../../lib/escapeHTML'; + const renderers = {}; /** @@ -49,7 +51,7 @@ Template.renderField.helpers({ html = Blaze.toHTMLWithData(Template[renderers[field.type]], { field, message }); } else { // consider the value already formatted as html - html = field.value; + html = escapeHTML(field.value); } return `
${ html }
`; }, diff --git a/lib/escapeHTML.spec.ts b/lib/escapeHTML.spec.ts new file mode 100644 index 000000000000..a153309fc758 --- /dev/null +++ b/lib/escapeHTML.spec.ts @@ -0,0 +1,19 @@ +import assert from 'assert'; + +import { describe, it } from 'mocha'; + +import { escapeHTML } from './escapeHTML'; + +describe('escapeHTML', () => { + it('works', () => { + assert.strictEqual(escapeHTML('
Blah & "blah" & \'blah\'
'), '<div>Blah & "blah" & 'blah'</div>'); + assert.strictEqual(escapeHTML('<'), '&lt;'); + assert.strictEqual(escapeHTML(' '), ' '); + assert.strictEqual(escapeHTML('¢'), '¢'); + assert.strictEqual(escapeHTML('¢ £ ¥ € © ®'), '¢ £ ¥ € © ®'); + assert.strictEqual(escapeHTML(5 as unknown as string), '5'); + assert.strictEqual(escapeHTML(''), ''); + assert.strictEqual(escapeHTML(null as unknown as string), ''); + assert.strictEqual(escapeHTML(undefined as unknown as string), ''); + }); +}); diff --git a/lib/escapeHTML.ts b/lib/escapeHTML.ts new file mode 100644 index 000000000000..00ac1f5a2a29 --- /dev/null +++ b/lib/escapeHTML.ts @@ -0,0 +1,27 @@ +const characterToHtmlEntityCode = { + '¢': 'cent', + '£': 'pound', + '¥': 'yen', + '€': 'euro', + '©': 'copy', + '®': 'reg', + '<': 'lt', + '>': 'gt', + '"': 'quot', + '&': 'amp', + '\'': '#39', +} as const; + +const regex = new RegExp(`[${ Object.keys(characterToHtmlEntityCode).join('') }]`, 'g'); + +const toString = (object: unknown): string => + (object ? `${ object }` : ''); + +const isEscapable = (char: string): char is keyof typeof characterToHtmlEntityCode => + char in characterToHtmlEntityCode; + +const escapeChar = (char: string): string => + (isEscapable(char) ? `&${ characterToHtmlEntityCode[char] };` : ''); + +export const escapeHTML = (str: string): string => + toString(str).replace(regex, escapeChar);