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('<'), '<');
+ 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);