diff --git a/webapp/tests/utils/formatting_channel_links.test.jsx b/webapp/tests/utils/formatting_channel_links.test.jsx new file mode 100644 index 0000000000..39dddf0083 --- /dev/null +++ b/webapp/tests/utils/formatting_channel_links.test.jsx @@ -0,0 +1,49 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import assert from 'assert'; + +import * as TextFormatting from 'utils/text_formatting.jsx'; + +describe('TextFormatting.ChannelLinks', () => { + it('Not channel links', (done) => { + assert.equal( + TextFormatting.formatText('~123').trim(), + '

~123

' + ); + + assert.equal( + TextFormatting.formatText('~town-square').trim(), + '

~town-square

' + ); + + done(); + }); + + it('Channel links', (done) => { + assert.equal( + TextFormatting.formatText('~town-square', { + channelNamesMap: {'town-square': {display_name: 'Town Square'}}, + team: {name: 'myteam'} + }).trim(), + '

~Town Square

' + ); + assert.equal( + TextFormatting.formatText('~town-square.', { + channelNamesMap: {'town-square': {display_name: 'Town Square'}}, + team: {name: 'myteam'} + }).trim(), + '

~Town Square.

' + ); + + assert.equal( + TextFormatting.formatText('~town-square', { + channelNamesMap: {'town-square': {display_name: 'Reception'}}, + team: {name: 'myteam'} + }).trim(), + '

~<b>Reception</b>

' + ); + + done(); + }); +}); diff --git a/webapp/utils/text_formatting.jsx b/webapp/utils/text_formatting.jsx index 4602a31b2f..e34b8fdbbb 100644 --- a/webapp/utils/text_formatting.jsx +++ b/webapp/utils/text_formatting.jsx @@ -185,7 +185,7 @@ function autolinkChannelMentions(text, tokens, channelNamesMap, team) { } tokens.set(alias, { - value: `${displayName}`, + value: `~${displayName}`, originalText: mention }); return alias; @@ -196,7 +196,7 @@ function autolinkChannelMentions(text, tokens, channelNamesMap, team) { if (channelMentionExists(channelNameLower)) { // Exact match - const alias = addToken(channelNameLower, mention, '~' + channelNamesMap[channelNameLower].display_name); + const alias = addToken(channelNameLower, mention, escapeHtml(channelNamesMap[channelNameLower].display_name)); return spacer + alias; } @@ -209,7 +209,8 @@ function autolinkChannelMentions(text, tokens, channelNamesMap, team) { if (channelMentionExists(channelNameLower)) { const suffix = originalChannelName.substr(c - 1); - const alias = addToken(channelNameLower, '~' + channelNameLower, '~' + channelNamesMap[channelNameLower].display_name); + const alias = addToken(channelNameLower, '~' + channelNameLower, + escapeHtml(channelNamesMap[channelNameLower].display_name)); return spacer + alias + suffix; } } else { @@ -231,6 +232,18 @@ export function escapeRegex(text) { return text.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); } +const htmlEntities = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' +}; + +export function escapeHtml(text) { + return text.replace(/[&<>"']/g, (match) => htmlEntities[match]); +} + function highlightCurrentMentions(text, tokens, mentionKeys = []) { let output = text; diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx index b99f7b967b..aa98a9872d 100644 --- a/webapp/utils/utils.jsx +++ b/webapp/utils/utils.jsx @@ -242,10 +242,6 @@ export function extractFirstLink(text) { return ''; } -export function escapeRegExp(string) { - return string.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'); -} - // Taken from http://stackoverflow.com/questions/1068834/object-comparison-in-javascript and modified slightly export function areObjectsEqual(x, y) { let p;