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=