diff --git a/src/app/molecules/message/Message.jsx b/src/app/molecules/message/Message.jsx index 6e3def4..e4b6883 100644 --- a/src/app/molecules/message/Message.jsx +++ b/src/app/molecules/message/Message.jsx @@ -206,7 +206,13 @@ const MessageBody = React.memo(({ let content = null; if (isCustomHTML) { try { - content = twemojify(sanitizeCustomHtml(body), undefined, true, false, true); + content = twemojify( + sanitizeCustomHtml(initMatrix.matrixClient, body), + undefined, + true, + false, + true, + ); } catch { console.error('Malformed custom html: ', body); content = twemojify(body, undefined); diff --git a/src/client/state/RoomsInput.js b/src/client/state/RoomsInput.js index 6b085fb..e1d3acf 100644 --- a/src/client/state/RoomsInput.js +++ b/src/client/state/RoomsInput.js @@ -6,6 +6,7 @@ import { math } from 'micromark-extension-math'; import { encode } from 'blurhash'; import { getShortcodeToEmoji } from '../../app/organisms/emoji-board/custom-emoji'; import { mathExtensionHtml, spoilerExtension, spoilerExtensionHtml } from '../../util/markdown'; +import { sanitizeText } from '../../util/sanitize'; import cons from './cons'; import settings from './settings'; @@ -148,29 +149,25 @@ function findAndReplace(text, regex, filter, replace) { return copyText; } -function formatAndEmojifyText(mx, roomList, room, text) { +function formatUserPill(room, text) { const { userIdsToDisplayNames } = room.currentState; - const parentIds = roomList.getAllParentSpaces(room.roomId); - const parentRooms = [...parentIds].map((id) => mx.getRoom(id)); - const allEmoji = getShortcodeToEmoji(mx, [room, ...parentRooms]); - - let formattedText; - if (settings.isMarkdown) { - formattedText = getFormattedBody(text); - } else { - formattedText = text; - } - - formattedText = findAndReplace( - formattedText, + return findAndReplace( + text, MXID_REGEX, (match) => userIdsToDisplayNames[match[0]], (match) => ( `@${userIdsToDisplayNames[match[0]]}` ), ); - formattedText = findAndReplace( - formattedText, +} + +function formatEmoji(mx, room, roomList, text) { + const parentIds = roomList.getAllParentSpaces(room.roomId); + const parentRooms = [...parentIds].map((id) => mx.getRoom(id)); + const allEmoji = getShortcodeToEmoji(mx, [room, ...parentRooms]); + + return findAndReplace( + text, SHORTCODE_REGEX, (match) => allEmoji.has(match[1]), (match) => { @@ -191,8 +188,6 @@ function formatAndEmojifyText(mx, roomList, room, text) { return tag; }, ); - - return formattedText; } class RoomsInput extends EventEmitter { @@ -295,25 +290,27 @@ class RoomsInput extends EventEmitter { } if (this.getMessage(roomId).trim() !== '') { + const rawMessage = input.message; let content = { - body: input.message, + body: rawMessage, msgtype: 'm.text', }; // Apply formatting if relevant - const formattedBody = formatAndEmojifyText( - this.matrixClient, - this.roomList, - room, - input.message, - ); + let formattedBody = settings.isMarkdown + ? getFormattedBody(rawMessage) + : sanitizeText(rawMessage); + + formattedBody = formatUserPill(room, formattedBody); + formattedBody = formatEmoji(this.matrixClient, room, this.roomList, formattedBody); + content.body = findAndReplace( content.body, MXID_REGEX, (match) => room.currentState.userIdsToDisplayNames[match[0]], (match) => `@${room.currentState.userIdsToDisplayNames[match[0]]}`, ); - if (formattedBody !== input.message) { + if (formattedBody !== sanitizeText(rawMessage)) { // Formatting was applied, and we need to switch to custom HTML content.format = 'org.matrix.custom.html'; content.formatted_body = formattedBody; @@ -481,19 +478,19 @@ class RoomsInput extends EventEmitter { }; // Apply formatting if relevant - const formattedBody = formatAndEmojifyText( - this.matrixClient, - this.roomList, - room, - editedBody, - ); + let formattedBody = settings.isMarkdown + ? getFormattedBody(editedBody) + : sanitizeText(editedBody); + formattedBody = formatUserPill(room, formattedBody); + formattedBody = formatEmoji(this.matrixClient, room, this.roomList, formattedBody); + content.body = findAndReplace( content.body, MXID_REGEX, (match) => room.currentState.userIdsToDisplayNames[match[0]], (match) => `@${room.currentState.userIdsToDisplayNames[match[0]]}`, ); - if (formattedBody !== editedBody) { + if (formattedBody !== sanitizeText(editedBody)) { content.formatted_body = ` * ${formattedBody}`; content.format = 'org.matrix.custom.html'; content['m.new_content'].formatted_body = formattedBody; diff --git a/src/util/sanitize.js b/src/util/sanitize.js index 5351a1a..ade47ff 100644 --- a/src/util/sanitize.js +++ b/src/util/sanitize.js @@ -1,7 +1,7 @@ import sanitizeHtml from 'sanitize-html'; -import initMatrix from '../client/initMatrix'; const MAX_TAG_NESTING = 100; +let mx = null; const permittedHtmlTags = [ 'font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', @@ -54,7 +54,7 @@ function transformATag(tagName, attribs) { 'data-mx-pill': userId, }, }; - if (userId === initMatrix.matrixClient.getUserId()) { + if (userId === mx?.getUserId()) { pill.attribs['data-mx-ping'] = undefined; } return pill; @@ -76,17 +76,17 @@ function transformATag(tagName, attribs) { function transformImgTag(tagName, attribs) { const { src } = attribs; - const mx = initMatrix.matrixClient; return { tagName, attribs: { ...attribs, - src: src.startsWith('mxc://') ? mx.mxcUrlToHttp(src) : src, + src: src.startsWith('mxc://') ? mx?.mxcUrlToHttp(src) : src, }, }; } -export function sanitizeCustomHtml(body) { +export function sanitizeCustomHtml(matrixClient, body) { + mx = matrixClient; return sanitizeHtml(body, { allowedTags: permittedHtmlTags, allowedAttributes: permittedTagToAttributes,