diff --git a/package.json b/package.json index 0caff285e8..95b294c8a5 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "diff-match-patch": "^1.0.4", "emojibase-data": "^4.0.2", "emojibase-regex": "^3.0.0", + "escape-html": "^1.0.3", "file-saver": "^1.3.3", "filesize": "3.5.6", "flux": "2.1.1", diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index fac0a71617..55fd028980 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -1,6 +1,7 @@ /* Copyright 2017 New Vector Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,6 +24,7 @@ import {wantsDateSeparator} from '../../../DateUtils'; import {MatrixEvent, MatrixClient} from 'matrix-js-sdk'; import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; import SettingsStore from "../../../settings/SettingsStore"; +import escapeHtml from "escape-html"; // This component does no cycle detection, simply because the only way to make such a cycle would be to // craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would @@ -101,6 +103,15 @@ export default class ReplyThread extends React.Component { if (html) html = this.stripHTMLReply(html); } + if (!body) body = ""; // Always ensure we have a body, for reasons. + + // Escape the body to use as HTML below. + // We also run a nl2br over the result to fix the fallback representation. We do this + // after converting the text to safe HTML to avoid user-provided BR's from being converted. + if (!html) html = escapeHtml(body).replace(/\n/g, '
'); + + // dev note: do not rely on `body` being safe for HTML usage below. + const evLink = permalinkCreator.forEvent(ev.getId()); const userLink = makeUserPermalink(ev.getSender()); const mxid = ev.getSender(); @@ -110,7 +121,7 @@ export default class ReplyThread extends React.Component { case 'm.text': case 'm.notice': { html = `
In reply to ${mxid}` - + `
${html || body}
`; + + `
${html}`; const lines = body.trim().split('\n'); if (lines.length > 0) { lines[0] = `<${mxid}> ${lines[0]}`; @@ -140,7 +151,7 @@ export default class ReplyThread extends React.Component { break; case 'm.emote': { html = `
In reply to * ` - + `${mxid}
${html || body}
`; + + `${mxid}
${html}`; const lines = body.trim().split('\n'); if (lines.length > 0) { lines[0] = `* <${mxid}> ${lines[0]}`; diff --git a/yarn.lock b/yarn.lock index fa7868d270..60fef54c16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2862,7 +2862,7 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=