From 0943ffa0aac37b712f2d47362b6d6b1fe7a6b2ce Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 15 Oct 2024 10:55:03 +0000 Subject: [PATCH 1/5] Upgrade dependency to matrix-js-sdk@34.8.0 --- package.json | 2 +- yarn.lock | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index bf7ab8eb15..6636b29766 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "jsrsasign": "^11.0.0", "katex": "^0.16.0", "lodash": "^4.17.21", - "matrix-js-sdk": "34.7.0", + "matrix-js-sdk": "34.8.0", "matrix-react-sdk": "3.112.0", "matrix-widget-api": "^1.8.2", "react": "17.0.2", diff --git a/yarn.lock b/yarn.lock index 602ccc2009..afa29b6974 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8213,6 +8213,27 @@ matrix-js-sdk@34.7.0: unhomoglyph "^1.0.6" uuid "10" +matrix-js-sdk@34.8.0: + version "34.8.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-34.8.0.tgz#7ee18fb2ca22fb96d72a6720d0e80f9de12dd4c6" + integrity sha512-zHYPE+vb6PP/dnRa6X3ktXf34yQg/Pct9r5CSP263hA4tRg5sKEVQgnY7KDADaABTvNMb9h7BZ5Xm9vjXsowNQ== + dependencies: + "@babel/runtime" "^7.12.5" + "@matrix-org/matrix-sdk-crypto-wasm" "^9.0.0" + "@matrix-org/olm" "3.2.15" + another-json "^0.2.0" + bs58 "^6.0.0" + content-type "^1.0.4" + jwt-decode "^4.0.0" + loglevel "^1.7.1" + matrix-events-sdk "0.0.1" + matrix-widget-api "^1.8.2" + oidc-client-ts "^3.0.1" + p-retry "4" + sdp-transform "^2.14.1" + unhomoglyph "^1.0.6" + uuid "10" + matrix-mock-request@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/matrix-mock-request/-/matrix-mock-request-2.6.0.tgz#0855c10b250668ce542b697251087be2bcc23f92" From 8904453bbfffaaa57d61c5e023398cd409acbdcb Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 15 Oct 2024 11:04:13 +0000 Subject: [PATCH 2/5] Upgrade dependency to matrix-react-sdk@3.113.0 --- package.json | 2 +- yarn.lock | 31 +++++-------------------------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 6636b29766..4d8636a981 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "katex": "^0.16.0", "lodash": "^4.17.21", "matrix-js-sdk": "34.8.0", - "matrix-react-sdk": "3.112.0", + "matrix-react-sdk": "3.113.0", "matrix-widget-api": "^1.8.2", "react": "17.0.2", "react-dom": "17.0.2", diff --git a/yarn.lock b/yarn.lock index afa29b6974..8220b723fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8192,27 +8192,6 @@ matrix-events-sdk@0.0.1: resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd" integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA== -matrix-js-sdk@34.7.0: - version "34.7.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-34.7.0.tgz#243e4eacbedd98a1096135a75765756cda910b7b" - integrity sha512-epauE/ZwksDyadm+0vg+g1keRUo600H/b1MzDZbaIrCY9fELzq3fIWctq9IxMQE/EEPe9jjLiNDooGJT5JJ2Ag== - dependencies: - "@babel/runtime" "^7.12.5" - "@matrix-org/matrix-sdk-crypto-wasm" "^9.0.0" - "@matrix-org/olm" "3.2.15" - another-json "^0.2.0" - bs58 "^6.0.0" - content-type "^1.0.4" - jwt-decode "^4.0.0" - loglevel "^1.7.1" - matrix-events-sdk "0.0.1" - matrix-widget-api "^1.8.2" - oidc-client-ts "^3.0.1" - p-retry "4" - sdp-transform "^2.14.1" - unhomoglyph "^1.0.6" - uuid "10" - matrix-js-sdk@34.8.0: version "34.8.0" resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-34.8.0.tgz#7ee18fb2ca22fb96d72a6720d0e80f9de12dd4c6" @@ -8241,10 +8220,10 @@ matrix-mock-request@^2.5.0: dependencies: expect "^28.1.0" -matrix-react-sdk@3.112.0: - version "3.112.0" - resolved "https://registry.yarnpkg.com/matrix-react-sdk/-/matrix-react-sdk-3.112.0.tgz#653929e27554289b14e289113d97c6189c2f3db2" - integrity sha512-S7rwFJuTGi1lHOHYh2QEmzhLWrubNh+4LQ4K3Bpf/Oco1Hh0IdabMidvFWkSy0fUcrZRKN04Acif7iLem3IhSw== +matrix-react-sdk@3.113.0: + version "3.113.0" + resolved "https://registry.yarnpkg.com/matrix-react-sdk/-/matrix-react-sdk-3.113.0.tgz#66bb447d6ead469df191a9cd1a412210b18a2c95" + integrity sha512-ZMOcmcCRW1La3dp9Cqyuuk9qUP+OLJ4BCB2zkjwpUbSKO35e1ahFy+dYqtRNFbCz3C2Auzw3ED9kV2uLFkTxLg== dependencies: "@babel/runtime" "^7.12.5" "@matrix-org/analytics-events" "^0.25.0" @@ -8289,7 +8268,7 @@ matrix-react-sdk@3.112.0: maplibre-gl "^2.0.0" matrix-encrypt-attachment "^1.0.3" matrix-events-sdk "0.0.1" - matrix-js-sdk "34.7.0" + matrix-js-sdk "34.8.0" matrix-widget-api "^1.9.0" memoize-one "^6.0.0" minimist "^1.2.5" From 8d7f2b5c1301129a488d3597f3839bd74203ee62 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 15 Oct 2024 12:37:21 +0100 Subject: [PATCH 3/5] Fix for CVE-2024-47779 / GHSA-3jm3-x98c-r34x --- src/serviceworker/index.ts | 48 +++++++++++++++++++----------- src/vector/platform/WebPlatform.ts | 3 ++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/serviceworker/index.ts b/src/serviceworker/index.ts index 22e7700319..99c6232314 100644 --- a/src/serviceworker/index.ts +++ b/src/serviceworker/index.ts @@ -40,12 +40,15 @@ global.addEventListener("fetch", (event: FetchEvent) => { // Note: ideally we'd keep the request headers etc, but in practice we can't even see those details. // See https://stackoverflow.com/a/59152482 - let url = event.request.url; + const url = new URL(event.request.url); // We only intercept v3 download and thumbnail requests as presumably everything else is deliberate. // For example, `/_matrix/media/unstable` or `/_matrix/media/v3/preview_url` are something well within // the control of the application, and appear to be choices made at a higher level than us. - if (!url.includes("/_matrix/media/v3/download") && !url.includes("/_matrix/media/v3/thumbnail")) { + if ( + !url.pathname.startsWith("/_matrix/media/v3/download") && + !url.pathname.startsWith("/_matrix/media/v3/thumbnail") + ) { return; // not a URL we care about } @@ -53,34 +56,42 @@ global.addEventListener("fetch", (event: FetchEvent) => { // later on we need to proxy the request through if it turns out the server doesn't support authentication. event.respondWith( (async (): Promise => { - let accessToken: string | undefined; + let auth: { accessToken?: string; homeserver: string } | undefined; try { // Figure out which homeserver we're communicating with - const csApi = url.substring(0, url.indexOf("/_matrix/media/v3")); + const csApi = url.origin; // Add jitter to reduce request spam, particularly to `/versions` on initial page load await new Promise((resolve) => setTimeout(() => resolve(), Math.random() * 10)); - // Locate our access token, and populate the fetchConfig with the authentication header. + // Locate the access token and homeserver url // @ts-expect-error - service worker types are not available. See 'fetch' event handler. const client = await global.clients.get(event.clientId); - accessToken = await getAccessToken(client); + auth = await getAuthData(client); + + // Is this request actually going to the homeserver? + const isRequestToHomeServer = url.origin === new URL(auth.homeserver).origin; + if (!isRequestToHomeServer) { + throw new Error("Request appears to be for media endpoint but wrong homeserver!"); + } // Update or populate the server support map using a (usually) authenticated `/versions` call. - await tryUpdateServerSupportMap(csApi, accessToken); + await tryUpdateServerSupportMap(csApi, auth.accessToken); // If we have server support (and a means of authentication), rewrite the URL to use MSC3916 endpoints. - if (serverSupportMap[csApi].supportsAuthedMedia && accessToken) { - url = url.replace(/\/media\/v3\/(.*)\//, "/client/v1/media/$1/"); + if (serverSupportMap[csApi].supportsAuthedMedia && auth.accessToken) { + url.href = url.href.replace(/\/media\/v3\/(.*)\//, "/client/v1/media/$1/"); } // else by default we make no changes } catch (err) { + // In case of some error, we stay safe by not adding the access-token to the request. + auth = undefined; console.error("SW: Error in request rewrite.", err); } // Add authentication and send the request. We add authentication even if MSC3916 endpoints aren't // being used to ensure patches like this work: // https://github.com/matrix-org/synapse/commit/2390b66bf0ec3ff5ffb0c7333f3c9b239eeb92bb - return fetch(url, fetchConfigForToken(accessToken)); + return fetch(url, fetchConfigForToken(auth?.accessToken)); })(), ); }); @@ -106,35 +117,36 @@ async function tryUpdateServerSupportMap(clientApiUrl: string, accessToken?: str // Ideally we'd use the `Client` interface for `client`, but since it's not available (see 'fetch' listener), we use // unknown for now and force-cast it to something close enough later. -async function getAccessToken(client: unknown): Promise { +async function getAuthData(client: unknown): Promise<{ accessToken: string; homeserver: string }> { // Access tokens are encrypted at rest, so while we can grab the "access token", we'll need to do work to get the // real thing. const encryptedAccessToken = await idbLoad("account", "mx_access_token"); // We need to extract a user ID and device ID from localstorage, which means calling WebPlatform for the // read operation. Service workers can't access localstorage. - const { userId, deviceId } = await askClientForUserIdParams(client); + const { userId, deviceId, homeserver } = await askClientForUserIdParams(client); // ... and this is why we need the user ID and device ID: they're index keys for the pickle key table. const pickleKeyData = await idbLoad("pickleKey", [userId, deviceId]); if (pickleKeyData && (!pickleKeyData.encrypted || !pickleKeyData.iv || !pickleKeyData.cryptoKey)) { - console.error("SW: Invalid pickle key loaded - ignoring"); - return undefined; + throw new Error("SW: Invalid pickle key loaded - ignoring"); } // Finally, try decrypting the thing and return that. This may fail, but that's okay. try { const pickleKey = await buildAndEncodePickleKey(pickleKeyData, userId, deviceId); - return tryDecryptToken(pickleKey, encryptedAccessToken, ACCESS_TOKEN_IV); + const accessToken = await tryDecryptToken(pickleKey, encryptedAccessToken, ACCESS_TOKEN_IV); + return { accessToken, homeserver }; } catch (e) { - console.error("SW: Error decrypting access token.", e); - return undefined; + throw new Error("SW: Error decrypting access token.", { cause: e }); } } // Ideally we'd use the `Client` interface for `client`, but since it's not available (see 'fetch' listener), we use // unknown for now and force-cast it to something close enough inside the function. -async function askClientForUserIdParams(client: unknown): Promise<{ userId: string; deviceId: string }> { +async function askClientForUserIdParams( + client: unknown, +): Promise<{ userId: string; deviceId: string; homeserver: string }> { return new Promise((resolve, reject) => { // Dev note: this uses postMessage, which is a highly insecure channel. postMessage is typically visible to other // tabs, windows, browser extensions, etc, making it far from ideal for sharing sensitive information. This is diff --git a/src/vector/platform/WebPlatform.ts b/src/vector/platform/WebPlatform.ts index e00704ecfd..fc22be677f 100644 --- a/src/vector/platform/WebPlatform.ts +++ b/src/vector/platform/WebPlatform.ts @@ -14,6 +14,7 @@ import { Action } from "matrix-react-sdk/src/dispatcher/actions"; import { CheckUpdatesPayload } from "matrix-react-sdk/src/dispatcher/payloads/CheckUpdatesPayload"; import UAParser from "ua-parser-js"; import { logger } from "matrix-js-sdk/src/logger"; +import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg"; import VectorBasePlatform from "./VectorBasePlatform"; import { parseQs } from "../url_utils"; @@ -62,10 +63,12 @@ export default class WebPlatform extends VectorBasePlatform { if (event.data?.["type"] === "userinfo" && event.data?.["responseKey"]) { const userId = localStorage.getItem("mx_user_id"); const deviceId = localStorage.getItem("mx_device_id"); + const homeserver = MatrixClientPeg.get()?.getHomeserverUrl(); event.source!.postMessage({ responseKey: event.data["responseKey"], userId, deviceId, + homeserver, }); } } catch (e) { From 63c8550791a0221189f495d6458fee7db601c789 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 15 Oct 2024 12:38:16 +0100 Subject: [PATCH 4/5] Supporting change for CVE-2024-47771 / GHSA-963w-49j9-gxj6 --- src/@types/global.d.ts | 1 + src/vector/platform/ElectronPlatform.tsx | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 4d36054ce8..a5b3189334 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -26,6 +26,7 @@ type ElectronChannel = | "userDownloadAction" | "openDesktopCapturerSourcePicker" | "userAccessToken" + | "homeserverUrl" | "serverSupportedVersions"; declare global { diff --git a/src/vector/platform/ElectronPlatform.tsx b/src/vector/platform/ElectronPlatform.tsx index 0f650e7dbb..3772497597 100644 --- a/src/vector/platform/ElectronPlatform.tsx +++ b/src/vector/platform/ElectronPlatform.tsx @@ -126,6 +126,11 @@ export default class ElectronPlatform extends VectorBasePlatform { window.electron!.send("userAccessToken", MatrixClientPeg.get()?.getAccessToken()); }); + // `homeserverUrl` (IPC) is requested by the main process. A reply is sent over the same channel. + window.electron.on("homeserverUrl", () => { + window.electron!.send("homeserverUrl", MatrixClientPeg.get()?.getHomeserverUrl()); + }); + // `serverSupportedVersions` is requested by the main process when it needs to know if the // server supports a particular version. This is primarily used to detect authenticated media // support. A reply is sent over the same channel. From 89380ab59ff25277ec2fa0287c9c16275cdf5283 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 15 Oct 2024 11:43:34 +0000 Subject: [PATCH 5/5] v1.11.81 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5911265dc8..ef18822d38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Changes in [1.11.81](https://github.com/element-hq/element-web/releases/tag/v1.11.81) (2024-10-15) +================================================================================================== +This release fixes High severity vulnerability CVE-2024-47771 / GHSA-963w-49j9-gxj6 + Changes in [1.11.80](https://github.com/element-hq/element-web/releases/tag/v1.11.80) (2024-10-08) ================================================================================================== ## ✨ Features diff --git a/package.json b/package.json index 4d8636a981..80325f101f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "element-web", - "version": "1.11.80", + "version": "1.11.81", "description": "A feature-rich client for Matrix.org", "author": "New Vector Ltd.", "repository": {