Skip to content

Commit 6d1b702

Browse files
committed
Merge branch 'master' into develop
2 parents 1e2e884 + bbe474a commit 6d1b702

File tree

11 files changed

+328
-88
lines changed

11 files changed

+328
-88
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
Changes in [1.11.85](https://github.com/element-hq/element-web/releases/tag/v1.11.85) (2024-11-12)
2+
==================================================================================================
3+
# Security
4+
- Fixes for [CVE-2024-51750](https://www.cve.org/CVERecord?id=CVE-2024-51750) / [GHSA-w36j-v56h-q9pc](https://github.com/element-hq/element-web/security/advisories/GHSA-w36j-v56h-q9pc)
5+
- Fixes for [CVE-2024-51749](https://www.cve.org/CVERecord?id=CVE-2024-51749) / [GHSA-5486-384g-mcx2](https://github.com/element-hq/element-web/security/advisories/GHSA-5486-384g-mcx2)
6+
- Update JS SDK with the fixes for [CVE-2024-50336](https://www.cve.org/CVERecord?id=CVE-2024-50336) / [GHSA-xvg8-m4x3-w6xr](https://github.com/matrix-org/matrix-js-sdk/security/advisories/GHSA-xvg8-m4x3-w6xr)
7+
8+
19
Changes in [1.11.84](https://github.com/element-hq/element-web/releases/tag/v1.11.84) (2024-11-05)
210
==================================================================================================
311
## ✨ Features

jest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const config: Config = {
3838
"recorderWorkletFactory": "<rootDir>/__mocks__/empty.js",
3939
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
4040
},
41-
transformIgnorePatterns: ["/node_modules/(?!matrix-js-sdk).+$"],
41+
transformIgnorePatterns: ["/node_modules/(?!(mime|matrix-js-sdk)).+$"],
4242
collectCoverageFrom: [
4343
"<rootDir>/src/**/*.{js,ts,tsx}",
4444
// getSessionLock is piped into a different JS context via stringification, and the coverage functionality is

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "element-web",
3-
"version": "1.11.84",
3+
"version": "1.11.85",
44
"description": "A feature-rich client for Matrix.org",
55
"author": "New Vector Ltd.",
66
"repository": {
@@ -125,6 +125,7 @@
125125
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
126126
"matrix-widget-api": "^1.10.0",
127127
"memoize-one": "^6.0.0",
128+
"mime": "^4.0.4",
128129
"oidc-client-ts": "^3.0.1",
129130
"opus-recorder": "^8.0.3",
130131
"pako": "^2.0.3",

playwright/e2e/widgets/stickers.spec.ts

Lines changed: 101 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,48 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
66
Please see LICENSE files in the repository root for full details.
77
*/
88

9+
import * as fs from "node:fs";
10+
911
import type { Page } from "@playwright/test";
1012
import { test, expect } from "../../element-web-test";
1113
import { ElementAppPage } from "../../pages/ElementAppPage";
14+
import { Credentials } from "../../plugins/homeserver";
1215

1316
const STICKER_PICKER_WIDGET_ID = "fake-sticker-picker";
1417
const STICKER_PICKER_WIDGET_NAME = "Fake Stickers";
1518
const STICKER_NAME = "Test Sticker";
1619
const ROOM_NAME_1 = "Sticker Test";
1720
const ROOM_NAME_2 = "Sticker Test Two";
18-
const STICKER_MESSAGE = JSON.stringify({
19-
action: "m.sticker",
20-
api: "fromWidget",
21-
data: {
22-
name: "teststicker",
23-
description: STICKER_NAME,
24-
file: "test.png",
25-
content: {
26-
body: STICKER_NAME,
27-
msgtype: "m.sticker",
28-
url: "mxc://localhost/somewhere",
21+
const STICKER_IMAGE = fs.readFileSync("playwright/sample-files/riot.png");
22+
23+
function getStickerMessage(contentUri: string, mimetype: string): string {
24+
return JSON.stringify({
25+
action: "m.sticker",
26+
api: "fromWidget",
27+
data: {
28+
name: "teststicker",
29+
description: STICKER_NAME,
30+
file: "test.png",
31+
content: {
32+
body: STICKER_NAME,
33+
info: {
34+
h: 480,
35+
mimetype: mimetype,
36+
size: 13818,
37+
w: 480,
38+
},
39+
msgtype: "m.sticker",
40+
url: contentUri,
41+
},
2942
},
30-
},
31-
requestId: "1",
32-
widgetId: STICKER_PICKER_WIDGET_ID,
33-
});
34-
const WIDGET_HTML = `
43+
requestId: "1",
44+
widgetId: STICKER_PICKER_WIDGET_ID,
45+
});
46+
}
47+
48+
function getWidgetHtml(contentUri: string, mimetype: string) {
49+
const stickerMessage = getStickerMessage(contentUri, mimetype);
50+
return `
3551
<html lang="en">
3652
<head>
3753
<title>Fake Sticker Picker</title>
@@ -51,13 +67,13 @@ const WIDGET_HTML = `
5167
<button name="Send" id="sendsticker">Press for sticker</button>
5268
<script>
5369
document.getElementById('sendsticker').onclick = () => {
54-
window.parent.postMessage(${STICKER_MESSAGE}, '*')
70+
window.parent.postMessage(${stickerMessage}, '*')
5571
};
5672
</script>
5773
</body>
5874
</html>
5975
`;
60-
76+
}
6177
async function openStickerPicker(app: ElementAppPage) {
6278
const options = await app.openMessageComposerOptions();
6379
await options.getByRole("menuitem", { name: "Sticker" }).click();
@@ -71,7 +87,8 @@ async function sendStickerFromPicker(page: Page) {
7187
await expect(page.locator(".mx_AppTileFullWidth#stickers")).not.toBeVisible();
7288
}
7389

74-
async function expectTimelineSticker(page: Page, roomId: string) {
90+
async function expectTimelineSticker(page: Page, roomId: string, contentUri: string) {
91+
const contentId = contentUri.split("/").slice(-1)[0];
7592
// Make sure it's in the right room
7693
await expect(page.locator(".mx_EventTile_sticker > a")).toHaveAttribute("href", new RegExp(`/${roomId}/`));
7794

@@ -80,13 +97,43 @@ async function expectTimelineSticker(page: Page, roomId: string) {
8097
// download URL.
8198
await expect(page.locator(`img[alt="${STICKER_NAME}"]`)).toHaveAttribute(
8299
"src",
83-
new RegExp("/download/localhost/somewhere"),
100+
new RegExp(`/localhost/${contentId}`),
84101
);
85102
}
86103

104+
async function expectFileTile(page: Page, roomId: string, contentUri: string) {
105+
await expect(page.locator(".mx_MFileBody_info_filename")).toContainText(STICKER_NAME);
106+
}
107+
108+
async function setWidgetAccountData(
109+
app: ElementAppPage,
110+
user: Credentials,
111+
stickerPickerUrl: string,
112+
provideCreatorUserId: boolean = true,
113+
) {
114+
await app.client.setAccountData("m.widgets", {
115+
[STICKER_PICKER_WIDGET_ID]: {
116+
content: {
117+
type: "m.stickerpicker",
118+
name: STICKER_PICKER_WIDGET_NAME,
119+
url: stickerPickerUrl,
120+
creatorUserId: provideCreatorUserId ? user.userId : undefined,
121+
},
122+
sender: user.userId,
123+
state_key: STICKER_PICKER_WIDGET_ID,
124+
type: "m.widget",
125+
id: STICKER_PICKER_WIDGET_ID,
126+
},
127+
});
128+
}
129+
87130
test.describe("Stickers", () => {
88131
test.use({
89132
displayName: "Sally",
133+
room: async ({ app }, use) => {
134+
const roomId = await app.client.createRoom({ name: ROOM_NAME_1 });
135+
await use({ roomId });
136+
},
90137
});
91138

92139
// We spin up a web server for the sticker picker so that we're not testing to see if
@@ -96,66 +143,60 @@ test.describe("Stickers", () => {
96143
//
97144
// See sendStickerFromPicker() for more detail on iframe comms.
98145
let stickerPickerUrl: string;
99-
test.beforeEach(async ({ webserver }) => {
100-
stickerPickerUrl = webserver.start(WIDGET_HTML);
101-
});
102146

103-
test("should send a sticker to multiple rooms", async ({ page, app, user }) => {
104-
const roomId1 = await app.client.createRoom({ name: ROOM_NAME_1 });
147+
test("should send a sticker to multiple rooms", async ({ webserver, page, app, user, room }) => {
105148
const roomId2 = await app.client.createRoom({ name: ROOM_NAME_2 });
106-
107-
await app.client.setAccountData("m.widgets", {
108-
[STICKER_PICKER_WIDGET_ID]: {
109-
content: {
110-
type: "m.stickerpicker",
111-
name: STICKER_PICKER_WIDGET_NAME,
112-
url: stickerPickerUrl,
113-
creatorUserId: user.userId,
114-
},
115-
sender: user.userId,
116-
state_key: STICKER_PICKER_WIDGET_ID,
117-
type: "m.widget",
118-
id: STICKER_PICKER_WIDGET_ID,
119-
},
120-
});
149+
const { content_uri: contentUri } = await app.client.uploadContent(STICKER_IMAGE, { type: "image/png" });
150+
const widgetHtml = getWidgetHtml(contentUri, "image/png");
151+
stickerPickerUrl = webserver.start(widgetHtml);
152+
setWidgetAccountData(app, user, stickerPickerUrl);
121153

122154
await app.viewRoomByName(ROOM_NAME_1);
123-
await expect(page).toHaveURL(`/#/room/${roomId1}`);
155+
await expect(page).toHaveURL(`/#/room/${room.roomId}`);
124156
await openStickerPicker(app);
125157
await sendStickerFromPicker(page);
126-
await expectTimelineSticker(page, roomId1);
158+
await expectTimelineSticker(page, room.roomId, contentUri);
127159

128160
// Ensure that when we switch to a different room that the sticker
129161
// goes to the right place
130162
await app.viewRoomByName(ROOM_NAME_2);
131163
await expect(page).toHaveURL(`/#/room/${roomId2}`);
132164
await openStickerPicker(app);
133165
await sendStickerFromPicker(page);
134-
await expectTimelineSticker(page, roomId2);
166+
await expectTimelineSticker(page, roomId2, contentUri);
135167
});
136168

137-
test("should handle a sticker picker widget missing creatorUserId", async ({ page, app, user }) => {
138-
const roomId1 = await app.client.createRoom({ name: ROOM_NAME_1 });
169+
test("should handle a sticker picker widget missing creatorUserId", async ({
170+
webserver,
171+
page,
172+
app,
173+
user,
174+
room,
175+
}) => {
176+
const { content_uri: contentUri } = await app.client.uploadContent(STICKER_IMAGE, { type: "image/png" });
177+
const widgetHtml = getWidgetHtml(contentUri, "image/png");
178+
stickerPickerUrl = webserver.start(widgetHtml);
179+
setWidgetAccountData(app, user, stickerPickerUrl, false);
139180

140-
await app.client.setAccountData("m.widgets", {
141-
[STICKER_PICKER_WIDGET_ID]: {
142-
content: {
143-
type: "m.stickerpicker",
144-
name: STICKER_PICKER_WIDGET_NAME,
145-
url: stickerPickerUrl,
146-
// No creatorUserId
147-
},
148-
sender: user.userId,
149-
state_key: STICKER_PICKER_WIDGET_ID,
150-
type: "m.widget",
151-
id: STICKER_PICKER_WIDGET_ID,
152-
},
181+
await app.viewRoomByName(ROOM_NAME_1);
182+
await expect(page).toHaveURL(`/#/room/${room.roomId}`);
183+
await openStickerPicker(app);
184+
await sendStickerFromPicker(page);
185+
await expectTimelineSticker(page, room.roomId, contentUri);
186+
});
187+
188+
test("should render invalid mimetype as a file", async ({ webserver, page, app, user, room }) => {
189+
const { content_uri: contentUri } = await app.client.uploadContent(STICKER_IMAGE, {
190+
type: "application/octet-stream",
153191
});
192+
const widgetHtml = getWidgetHtml(contentUri, "application/octet-stream");
193+
stickerPickerUrl = webserver.start(widgetHtml);
194+
setWidgetAccountData(app, user, stickerPickerUrl);
154195

155196
await app.viewRoomByName(ROOM_NAME_1);
156-
await expect(page).toHaveURL(`/#/room/${roomId1}`);
197+
await expect(page).toHaveURL(`/#/room/${room.roomId}`);
157198
await openStickerPicker(app);
158199
await sendStickerFromPicker(page);
159-
await expectTimelineSticker(page, roomId1);
200+
await expectFileTile(page, room.roomId, contentUri);
160201
});
161202
});

src/components/views/messages/DateSeparator.tsx

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,25 +98,29 @@ export default class DateSeparator extends React.Component<IProps, IState> {
9898
}
9999

100100
private getLabel(): string {
101-
const date = new Date(this.props.ts);
102-
const disableRelativeTimestamps = !SettingsStore.getValue(UIFeature.TimelineEnableRelativeDates);
103-
104-
// During the time the archive is being viewed, a specific day might not make sense, so we return the full date
105-
if (this.props.forExport || disableRelativeTimestamps) return formatFullDateNoTime(date);
106-
107-
const today = new Date();
108-
const yesterday = new Date();
109-
const days = getDaysArray("long");
110-
yesterday.setDate(today.getDate() - 1);
111-
112-
if (date.toDateString() === today.toDateString()) {
113-
return this.relativeTimeFormat.format(0, "day"); // Today
114-
} else if (date.toDateString() === yesterday.toDateString()) {
115-
return this.relativeTimeFormat.format(-1, "day"); // Yesterday
116-
} else if (today.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
117-
return days[date.getDay()]; // Sunday-Saturday
118-
} else {
119-
return formatFullDateNoTime(date);
101+
try {
102+
const date = new Date(this.props.ts);
103+
const disableRelativeTimestamps = !SettingsStore.getValue(UIFeature.TimelineEnableRelativeDates);
104+
105+
// During the time the archive is being viewed, a specific day might not make sense, so we return the full date
106+
if (this.props.forExport || disableRelativeTimestamps) return formatFullDateNoTime(date);
107+
108+
const today = new Date();
109+
const yesterday = new Date();
110+
const days = getDaysArray("long");
111+
yesterday.setDate(today.getDate() - 1);
112+
113+
if (date.toDateString() === today.toDateString()) {
114+
return this.relativeTimeFormat.format(0, "day"); // Today
115+
} else if (date.toDateString() === yesterday.toDateString()) {
116+
return this.relativeTimeFormat.format(-1, "day"); // Yesterday
117+
} else if (today.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
118+
return days[date.getDay()]; // Sunday-Saturday
119+
} else {
120+
return formatFullDateNoTime(date);
121+
}
122+
} catch {
123+
return _t("common|message_timestamp_invalid");
120124
}
121125
}
122126

0 commit comments

Comments
 (0)