diff --git a/src/utils/exportUtils/HtmlExport.tsx b/src/utils/exportUtils/HtmlExport.tsx
index 667978b7b0..41edfd93df 100644
--- a/src/utils/exportUtils/HtmlExport.tsx
+++ b/src/utils/exportUtils/HtmlExport.tsx
@@ -21,6 +21,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { renderToStaticMarkup } from "react-dom/server";
import { EventType, MsgType } from "matrix-js-sdk/src/@types/event";
import { logger } from "matrix-js-sdk/src/logger";
+import escapeHtml from "escape-html";
import Exporter from "./Exporter";
import { mediaFromMxc } from "../../customisations/Media";
@@ -97,11 +98,16 @@ export default class HTMLExporter extends Exporter {
const exporter = this.room.client.getSafeUserId();
const exporterName = this.room.getMember(exporter)?.rawDisplayName;
const topic = this.room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic || "";
- const createdText = _t("%(creatorName)s created this room.", {
- creatorName,
- });
- const exportedText = renderToStaticMarkup(
+ const safeCreatedText = escapeHtml(
+ _t("%(creatorName)s created this room.", {
+ creatorName,
+ }),
+ );
+ const safeExporter = escapeHtml(exporter);
+ const safeRoomName = escapeHtml(this.room.name);
+ const safeTopic = escapeHtml(topic);
+ const safeExportedText = renderToStaticMarkup(
{_t(
"This is the start of export of . Exported by at %(exportDate)s.",
@@ -109,16 +115,19 @@ export default class HTMLExporter extends Exporter {
exportDate,
},
{
- roomName: () => {this.room.name},
+ roomName: () => {safeRoomName},
exporterDetails: () => (
-
+
{exporterName ? (
<>
- {exporterName}
- {" (" + exporter + ")"}
+ {escapeHtml(exporterName)}I {" (" + safeExporter + ")"}
>
) : (
- {exporter}
+ {safeExporter}
)}
),
@@ -127,7 +136,7 @@ export default class HTMLExporter extends Exporter {
,
);
- const topicText = topic ? _t("Topic: %(topic)s", { topic }) : "";
+ const safeTopicText = topic ? _t("Topic: %(topic)s", { topic: safeTopic }) : "";
const previousMessagesLink = renderToStaticMarkup(
currentPage !== 0 ? (
@@ -183,12 +192,12 @@ export default class HTMLExporter extends Exporter {
-
+
${previousMessagesLink}
@@ -214,10 +223,10 @@ export default class HTMLExporter extends Exporter {
currentPage == 0
? `
${roomAvatar}
-
${this.room.name}
-
${createdText}
${exportedText}
+
${safeRoomName}
+
${safeCreatedText}
${safeExportedText}
-
${topicText}
+
${safeTopicText}
`
: ""
}
diff --git a/test/utils/exportUtils/HTMLExport-test.ts b/test/utils/exportUtils/HTMLExport-test.ts
index f81764170c..53512dbad1 100644
--- a/test/utils/exportUtils/HTMLExport-test.ts
+++ b/test/utils/exportUtils/HTMLExport-test.ts
@@ -25,6 +25,7 @@ import {
RoomState,
} from "matrix-js-sdk/src/matrix";
import fetchMock from "fetch-mock-jest";
+import escapeHtml from "escape-html";
import { filterConsole, mkStubRoom, REPEATABLE_DATE, stubClient } from "../../test-utils";
import { ExportType, IExportOptions } from "../../../src/utils/exportUtils/exportUtils";
@@ -505,4 +506,49 @@ describe("HTMLExport", () => {
);
expect(result).not.toContain("Next group of messages");
});
+
+ it("should not leak javascript from room names or topics", async () => {
+ const name = "