Compare commits

...

22 Commits

Author SHA1 Message Date
RiotRobot fb9e258468 v28.0.0 2023-08-29 10:53:33 +01:00
RiotRobot 974723ceef Prepare changelog for v28.0.0 2023-08-29 10:53:30 +01:00
RiotRobot 9b8dcf53ed v28.0.0-rc.1 2023-08-22 15:18:45 +01:00
RiotRobot 324af3ee67 Prepare changelog for v28.0.0-rc.1 2023-08-22 15:18:42 +01:00
RiotRobot ec6c0946d4 v27.3.0-rc.1 2023-08-22 15:13:46 +01:00
RiotRobot e5f480b032 Prepare changelog for v27.3.0-rc.1 2023-08-22 15:13:43 +01:00
Florian Duros 6bf4ed8672 ElementR: Add CryptoApi.requestVerificationDM (#3643)
* Add `CryptoApi.requestVerificationDM`

* Fix RoomMessageRequest url

* Review changes

* Merge fixes

* Add BOB test data

* `requestVerificationDM` test works against old crypto (encrypted verification request)

* Update test data
2023-08-21 14:48:32 +00:00
Valere c18d691ef5 RustCrypto | Implement keybackup loop (#3652)
* Implement `CryptoApi.checkKeyBackup`

* Deprecate `MatrixClient.enableKeyBackup`.

* fix integ test

* more tests

* Implement keybackup loop

* cleaning

* update matrix-sdk-crypto-wasm to 1.2.1

* fix lint

* avoid real timer stuff

* Simplify test

* post merge lint fix

* revert change on yarn.lock

* code review

* Generate test data for exported keys

* code review cleaning

* cleanup legacy backup loop

* Update spec/test-utils/test-data/generate-test-data.py

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* Update spec/test-utils/test-data/generate-test-data.py

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>

* update yarn.lock for new wasm bindings

---------

Co-authored-by: Richard van der Hoff <richard@matrix.org>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
2023-08-17 14:10:57 +00:00
ElementRobot 97cf73bc52 Resetting package fields for development 2023-08-15 13:19:02 +01:00
ElementRobot aa25103665 Merge branch 'master' into develop 2023-08-15 13:18:59 +01:00
ElementRobot 858db67778 v27.2.0 2023-08-15 13:17:57 +01:00
ElementRobot e230abee45 Prepare changelog for v27.2.0 2023-08-15 13:17:55 +01:00
Travis Ralston 8c16d69f3c Set minimum supported Matrix 1.1 version (drop legacy r0 versions) (#3007)
Co-authored-by: Germain <germains@element.io>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-08-14 09:25:15 +01:00
Michael Telatynski 55b9116c99 Re-export localStorage-crypto-store (#3660) 2023-08-11 06:02:52 +00:00
Florian Duros 3a5d66057e ElementR: Process all verification events, not just requests (#3650)
* Process all verification event

* Add test for `isVerificationEvent`

* Review changes

* Remove null comparison and add doc to remote echo

* review changes
2023-08-09 14:14:58 +00:00
Richard van der Hoff 3f7af189e4 Implement CryptoApi.checkKeyBackupAndEnable (#3633)
* Implement `CryptoApi.checkKeyBackup`

* Deprecate `MatrixClient.enableKeyBackup`.

* fix integ test

* more tests

---------

Co-authored-by: valere <valeref@matrix.org>
2023-08-09 09:59:03 +00:00
renovate[bot] 16ddcb0ed0 Lock file maintenance (#3659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-08 15:32:57 +00:00
renovate[bot] 9e35b8dd0a Update all non-major dependencies (#3656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-08 15:09:57 +00:00
renovate[bot] bed787b749 Update tibdex/backport digest to 9565281 (#3658)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-08 14:14:28 +00:00
renovate[bot] d260b8be56 Update dependency eslint-config-prettier to v9 (#3657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-08 14:10:12 +00:00
renovate[bot] 97991dad02 Update dependency @types/node to v18.17.3 (#3655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-08 14:09:52 +00:00
renovate[bot] b8c19c47ab Update babel monorepo to v7.22.10 (#3654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-08 14:07:45 +00:00
49 changed files with 1925 additions and 727 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
)
)
steps:
- uses: tibdex/backport@2e217641d82d02ba0603f46b1aeedefb258890ac # v2
- uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2
with:
labels_template: "<%= JSON.stringify([...labels, 'X-Release-Blocker']) %>"
# We can't use GITHUB_TOKEN here or CI won't run on the new PR
+1 -1
View File
@@ -15,7 +15,7 @@ concurrency:
jobs:
cypress:
name: Cypress
uses: matrix-org/matrix-react-sdk/.github/workflows/cypress.yaml@v3.76.0
uses: matrix-org/matrix-react-sdk/.github/workflows/cypress.yaml@v3.77.1
permissions:
actions: read
issues: read
+1 -1
View File
@@ -21,7 +21,7 @@ concurrency:
jobs:
build-element-web:
name: Build element-web
uses: matrix-org/matrix-react-sdk/.github/workflows/element-web.yaml@v3.76.0
uses: matrix-org/matrix-react-sdk/.github/workflows/element-web.yaml@v3.77.1
with:
matrix-js-sdk-sha: ${{ github.sha }}
react-sdk-repository: matrix-org/matrix-react-sdk
+18 -2
View File
@@ -1,5 +1,21 @@
Changes in [27.2.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v27.2.0-rc.1) (2023-08-08)
============================================================================================================
Changes in [28.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v28.0.0) (2023-08-29)
==================================================================================================
## 🚨 BREAKING CHANGES
* Set minimum supported Matrix 1.1 version (drop legacy r0 versions) ([\#3007](https://github.com/matrix-org/matrix-js-sdk/pull/3007)). Fixes vector-im/element-web#16876.
## 🦖 Deprecations
* **The Browserify artifact is being deprecated, scheduled for removal in the October 10th release cycle. (#3189)**
## ✨ Features
* ElementR: Add `CryptoApi.requestVerificationDM` ([\#3643](https://github.com/matrix-org/matrix-js-sdk/pull/3643)). Contributed by @florianduros.
* Implement `CryptoApi.checkKeyBackupAndEnable` ([\#3633](https://github.com/matrix-org/matrix-js-sdk/pull/3633)). Fixes vector-im/crypto-internal#111 and vector-im/crypto-internal#112.
## 🐛 Bug Fixes
* ElementR: Process all verification events, not just requests ([\#3650](https://github.com/matrix-org/matrix-js-sdk/pull/3650)). Contributed by @florianduros.
Changes in [27.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v27.2.0) (2023-08-15)
==================================================================================================
## 🦖 Deprecations
* **The Browserify artifact is being deprecated, scheduled for removal in the October 10th release cycle. (#3189)**
+2
View File
@@ -11,6 +11,8 @@
This is the [Matrix](https://matrix.org) Client-Server SDK for JavaScript and TypeScript. This SDK can be run in a
browser or in Node.js.
#### Minimum Matrix server version: v1.1
The Matrix specification is constantly evolving - while this SDK aims for maximum backwards compatibility, it only
guarantees that a feature will be supported for at least 4 spec releases. For example, if a feature the js-sdk supports
is removed in v1.4 then the feature is _eligible_ for removal from the SDK when v1.8 is released. This SDK has no
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "27.2.0-rc.1",
"version": "28.0.0",
"description": "Matrix Client-Server SDK for Javascript",
"engines": {
"node": ">=18.0.0"
@@ -55,7 +55,7 @@
],
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-wasm": "^1.2.0",
"@matrix-org/matrix-sdk-crypto-wasm": "^1.2.1",
"another-json": "^0.2.0",
"bs58": "^5.0.0",
"content-type": "^1.0.4",
@@ -101,9 +101,9 @@
"browserify-swap": "^0.2.2",
"debug": "^4.3.4",
"domexception": "^4.0.0",
"eslint": "8.45.0",
"eslint": "8.46.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-typescript": "^3.5.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^27.1.6",
+1 -1
View File
@@ -90,7 +90,7 @@ export class TestClient implements IE2EKeyReceiver, ISyncResponder {
logger.log(this + ": starting");
this.httpBackend.when("GET", "/versions").respond(200, {
// we have tests that rely on support for lazy-loading members
versions: ["r0.5.0"],
versions: ["v1.1"],
});
this.httpBackend.when("GET", "/pushrules").respond(200, {});
this.httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
+9 -8
View File
@@ -1945,7 +1945,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
};
};
for (const path of ["/_matrix/client/r0/keys/upload", "/_matrix/client/v3/keys/upload"]) {
for (const path of ["/_matrix/client/v3/keys/upload", "/_matrix/client/v3/keys/upload"]) {
fetchMock.post(new URL(path, aliceClient.getHomeserverUrl()).toString(), listener, {
// These routes are already defined in the E2EKeyReceiver
// We want to overwrite the behaviour of the E2EKeyReceiver
@@ -2082,9 +2082,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
return queryResponseBody;
};
for (const path of ["/_matrix/client/r0/keys/query", "/_matrix/client/v3/keys/query"]) {
fetchMock.post(new URL(path, aliceClient.getHomeserverUrl()).toString(), listener);
}
fetchMock.post(
new URL("/_matrix/client/v3/keys/query", aliceClient.getHomeserverUrl()).toString(),
listener,
);
});
}
@@ -2175,7 +2176,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
});
/**
* Create a mock to respond to the PUT request `/_matrix/client/r0/user/:userId/account_data/:type(m.secret_storage.*)`
* Create a mock to respond to the PUT request `/_matrix/client/v3/user/:userId/account_data/:type(m.secret_storage.*)`
* Resolved when a key is uploaded (ie in `body.content.key`)
* https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3useruseridaccount_datatype
*/
@@ -2184,7 +2185,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
// This url is called multiple times during the secret storage bootstrap process
// When we received the newly generated key, we return it
fetchMock.put(
"express:/_matrix/client/r0/user/:userId/account_data/:type(m.secret_storage.*)",
"express:/_matrix/client/v3/user/:userId/account_data/:type(m.secret_storage.*)",
(url: string, options: RequestInit) => {
const content = JSON.parse(options.body as string);
@@ -2200,7 +2201,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
}
/**
* Create a mock to respond to the PUT request `/_matrix/client/r0/user/:userId/account_data/m.cross_signing.${key}`
* Create a mock to respond to the PUT request `/_matrix/client/v3/user/:userId/account_data/m.cross_signing.${key}`
* Resolved when the cross signing key is uploaded
* https://spec.matrix.org/v1.6/client-server-api/#put_matrixclientv3useruseridaccount_datatype
*/
@@ -2208,7 +2209,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string,
return new Promise((resolve) => {
// Called when the cross signing key is uploaded
fetchMock.put(
`express:/_matrix/client/r0/user/:userId/account_data/m.cross_signing.${key}`,
`express:/_matrix/client/v3/user/:userId/account_data/m.cross_signing.${key}`,
(url: string, options: RequestInit) => {
const content = JSON.parse(options.body as string);
resolve(content.encrypted);
+431 -7
View File
@@ -19,7 +19,7 @@ import "fake-indexeddb/auto";
import { IDBFactory } from "fake-indexeddb";
import { IKeyBackupSession } from "../../../src/crypto/keybackup";
import { createClient, CryptoEvent, ICreateClientOpts, IEvent, MatrixClient } from "../../../src";
import { createClient, CryptoEvent, ICreateClientOpts, IEvent, MatrixClient, TypedEventEmitter } from "../../../src";
import { SyncResponder } from "../../test-utils/SyncResponder";
import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver";
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
@@ -27,6 +27,7 @@ import { mockInitialApiRequests } from "../../test-utils/mockEndpoints";
import { awaitDecryption, CRYPTO_BACKENDS, InitCrypto, syncPromise } from "../../test-utils/test-utils";
import * as testData from "../../test-utils/test-data";
import { KeyBackupInfo } from "../../../src/crypto-api/keybackup";
import { IKeyBackup } from "../../../src/crypto/backup";
const ROOM_ID = "!ROOM:ID";
@@ -83,6 +84,58 @@ afterEach(() => {
indexedDB = new IDBFactory();
});
enum MockKeyUploadEvent {
KeyUploaded = "KeyUploaded",
}
type MockKeyUploadEventHandlerMap = {
[MockKeyUploadEvent.KeyUploaded]: (roomId: string, sessionId: string, backupVersion: string) => void;
};
/*
* Test helper. Returns an event emitter that will emit an event every time fetchmock sees a request to backup a key.
*/
function mockUploadEmitter(
expectedVersion: string,
): TypedEventEmitter<MockKeyUploadEvent, MockKeyUploadEventHandlerMap> {
const emitter = new TypedEventEmitter();
fetchMock.put(
"path:/_matrix/client/v3/room_keys/keys",
(url, request) => {
const version = new URLSearchParams(new URL(url).search).get("version");
if (version != expectedVersion) {
return {
status: 403,
body: {
current_version: expectedVersion,
errcode: "M_WRONG_ROOM_KEYS_VERSION",
error: "Wrong backup version.",
},
};
}
const uploadPayload: IKeyBackup = JSON.parse(request.body?.toString() ?? "{}");
let count = 0;
for (const [roomId, value] of Object.entries(uploadPayload.rooms)) {
for (const sessionId of Object.keys(value.sessions)) {
emitter.emit(MockKeyUploadEvent.KeyUploaded, roomId, sessionId, version);
count++;
}
}
return {
status: 200,
body: {
count: count,
etag: "abcdefg",
},
};
},
{
overwriteRoutes: true,
},
);
return emitter;
}
describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backend: string, initCrypto: InitCrypto) => {
// oldBackendOnly is an alternative to `it` or `test` which will skip the test if we are running against the
// Rust backend. Once we have full support in the rust sdk, it will go away.
@@ -176,7 +229,256 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
expect(event.getContent()).toEqual("testytest");
});
oldBackendOnly("getActiveSessionBackupVersion() should give correct result", async function () {
describe("backupLoop", () => {
it("Alice should upload known keys when backup is enabled", async function () {
// 404 means that there is no active backup
fetchMock.get("path:/_matrix/client/v3/room_keys/version", 404);
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
await aliceClient.startClient();
// tell Alice to trust the dummy device that signed the backup
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
// check that signalling is working
const remainingZeroPromise = new Promise<void>((resolve, reject) => {
aliceClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => {
if (remaining == 0) {
resolve();
}
});
});
const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY;
const uploadMockEmitter = mockUploadEmitter(testData.SIGNED_BACKUP_DATA.version!);
const uploadPromises = someRoomKeys.map((data) => {
new Promise<void>((resolve) => {
uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => {
if (
data.room_id == roomId &&
data.session_id == sessionId &&
version == testData.SIGNED_BACKUP_DATA.version
) {
resolve();
}
});
});
});
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, {
overwriteRoutes: true,
});
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
await aliceCrypto.importRoomKeys(someRoomKeys);
// The backup loop is waiting a random amount of time to avoid different clients firing at the same time.
jest.runAllTimers();
await Promise.all(uploadPromises);
// Wait until all keys are backed up to ensure that when a new key is received the loop is restarted
await remainingZeroPromise;
// A new key import should trigger a new upload.
const newKey = testData.MEGOLM_SESSION_DATA;
const newKeyUploadPromise = new Promise<void>((resolve) => {
uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => {
if (
newKey.room_id == roomId &&
newKey.session_id == sessionId &&
version == testData.SIGNED_BACKUP_DATA.version
) {
resolve();
}
});
});
await aliceCrypto.importRoomKeys([newKey]);
jest.runAllTimers();
await newKeyUploadPromise;
});
it("Alice should re-upload all keys if a new trusted backup is available", async function () {
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
await aliceClient.startClient();
// tell Alice to trust the dummy device that signed the backup
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
// check that signalling is working
const remainingZeroPromise = new Promise<void>((resolve) => {
aliceClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => {
if (remaining == 0) {
resolve();
}
});
});
const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY;
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, {
overwriteRoutes: true,
});
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
mockUploadEmitter(testData.SIGNED_BACKUP_DATA.version!);
await aliceCrypto.importRoomKeys(someRoomKeys);
// The backup loop is waiting a random amount of time to avoid different clients firing at the same time.
jest.runAllTimers();
// wait for all keys to be backed up
await remainingZeroPromise;
const newBackupVersion = "2";
const uploadMockEmitter = mockUploadEmitter(newBackupVersion);
const newBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA));
newBackup.version = newBackupVersion;
// Let's simulate that a new backup is available by returning error code on key upload
fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, {
overwriteRoutes: true,
});
// If we import a new key the loop will try to upload to old version, it will
// fail then check the current version and switch if trusted
const uploadPromises = someRoomKeys.map((data) => {
new Promise<void>((resolve) => {
uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => {
if (data.room_id == roomId && data.session_id == sessionId && version == newBackupVersion) {
resolve();
}
});
});
});
const disableOldBackup = new Promise<void>((resolve) => {
aliceClient.on(CryptoEvent.KeyBackupFailed, (errCode) => {
if (errCode == "M_WRONG_ROOM_KEYS_VERSION") {
resolve();
}
});
});
const enableNewBackup = new Promise<void>((resolve) => {
aliceClient.on(CryptoEvent.KeyBackupStatus, (enabled) => {
if (enabled) {
resolve();
}
});
});
// A new key import should trigger a new upload.
const newKey = testData.MEGOLM_SESSION_DATA;
const newKeyUploadPromise = new Promise<void>((resolve) => {
uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => {
if (newKey.room_id == roomId && newKey.session_id == sessionId && version == newBackupVersion) {
resolve();
}
});
});
await aliceCrypto.importRoomKeys([newKey]);
jest.runAllTimers();
await disableOldBackup;
await enableNewBackup;
jest.runAllTimers();
await Promise.all(uploadPromises);
await newKeyUploadPromise;
});
it("Backup loop should be resistant to network failures", async function () {
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
await aliceClient.startClient();
// tell Alice to trust the dummy device that signed the backup
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, {
overwriteRoutes: true,
});
// on the first key upload attempt, simulate a network failure
const failurePromise = new Promise((resolve) => {
fetchMock.put(
"path:/_matrix/client/v3/room_keys/keys",
() => {
resolve(undefined);
throw new TypeError(`Failed to fetch`);
},
{
overwriteRoutes: true,
},
);
});
// kick the import loop off and wait for the failed request
const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY;
await aliceCrypto.importRoomKeys(someRoomKeys);
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
jest.runAllTimers();
await failurePromise;
// Fix the endpoint to do successful uploads
const successPromise = new Promise((resolve) => {
fetchMock.put(
"path:/_matrix/client/v3/room_keys/keys",
() => {
resolve(undefined);
return {
status: 200,
body: {
count: 2,
etag: "abcdefg",
},
};
},
{
overwriteRoutes: true,
},
);
});
// check that a `KeyBackupSessionsRemaining` event is emitted with `remaining == 0`
const allKeysUploadedPromise = new Promise((resolve) => {
aliceClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => {
if (remaining == 0) {
resolve(undefined);
}
});
});
// run the timers, which will make the backup loop redo the request
await jest.runAllTimersAsync();
await successPromise;
await allKeysUploadedPromise;
});
});
it("getActiveSessionBackupVersion() should give correct result", async function () {
// 404 means that there is no active backup
fetchMock.get("express:/_matrix/client/v3/room_keys/version", 404);
@@ -187,7 +489,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
// tell Alice to trust the dummy device that signed the backup
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
await aliceClient.checkKeyBackup();
await aliceCrypto.checkKeyBackupAndEnable();
// At this point there is no backup
let backupStatus: string | null;
@@ -201,9 +503,9 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
overwriteRoutes: true,
});
const checked = await aliceClient.checkKeyBackup();
const checked = await aliceCrypto.checkKeyBackupAndEnable();
expect(checked?.backupInfo?.version).toStrictEqual(unsignedBackup.version);
expect(checked?.trustInfo?.usable).toBeFalsy();
expect(checked?.trustInfo?.trusted).toBeFalsy();
backupStatus = await aliceCrypto.getActiveSessionBackupVersion();
expect(backupStatus).toBeNull();
@@ -222,8 +524,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
});
});
const validCheck = await aliceClient.checkKeyBackup();
expect(validCheck?.trustInfo?.usable).toStrictEqual(true);
const validCheck = await aliceCrypto.checkKeyBackupAndEnable();
expect(validCheck?.trustInfo?.trusted).toStrictEqual(true);
await backupPromise;
@@ -286,6 +588,128 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe
});
});
describe("checkKeyBackupAndEnable", () => {
it("enables a backup signed by a trusted device", async () => {
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
// tell Alice to trust the dummy device that signed the backup
await aliceClient.startClient();
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
expect(result!.trustInfo).toEqual({ trusted: true, matchesDecryptionKey: false });
expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version);
});
it("does not enable a backup signed by an untrusted device", async () => {
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
// download the device list, to match the trusted case
await aliceClient.startClient();
await waitForDeviceList();
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
expect(result!.trustInfo).toEqual({ trusted: false, matchesDecryptionKey: false });
expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull();
});
it("disables backup when a new untrusted backup is available", async () => {
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
// tell Alice to trust the dummy device that signed the backup
await aliceClient.startClient();
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version);
const unsignedBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA));
delete unsignedBackup.auth_data.signatures;
unsignedBackup.version = "2";
fetchMock.get("path:/_matrix/client/v3/room_keys/version", unsignedBackup, {
overwriteRoutes: true,
});
await aliceCrypto.checkKeyBackupAndEnable();
expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull();
});
it("switches backup when a new trusted backup is available", async () => {
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
// tell Alice to trust the dummy device that signed the backup
await aliceClient.startClient();
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version);
const newBackupVersion = "2";
const newBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA));
newBackup.version = newBackupVersion;
fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, {
overwriteRoutes: true,
});
await aliceCrypto.checkKeyBackupAndEnable();
expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(newBackupVersion);
});
it("Disables when backup is deleted", async () => {
aliceClient = await initTestClient();
const aliceCrypto = aliceClient.getCrypto()!;
// tell Alice to trust the dummy device that signed the backup
await aliceClient.startClient();
await waitForDeviceList();
await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID);
fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA);
const result = await aliceCrypto.checkKeyBackupAndEnable();
expect(result).toBeTruthy();
expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version);
fetchMock.get(
"path:/_matrix/client/v3/room_keys/version",
{
status: 404,
body: {
errcode: "M_NOT_FOUND",
error: "No backup found",
},
},
{
overwriteRoutes: true,
},
);
const noResult = await aliceCrypto.checkKeyBackupAndEnable();
expect(noResult).toBeNull();
expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull();
});
});
/** make sure that the client knows about the dummy device */
async function waitForDeviceList(): Promise<void> {
// Completing the initial sync will make the device list download outdated device lists (of which our own
+91 -2
View File
@@ -22,7 +22,15 @@ import fetchMock from "fetch-mock-jest";
import { IDBFactory } from "fake-indexeddb";
import { createHash } from "crypto";
import { createClient, CryptoEvent, ICreateClientOpts, MatrixClient } from "../../../src";
import {
createClient,
CryptoEvent,
IContent,
ICreateClientOpts,
MatrixClient,
MatrixEvent,
MatrixEventEvent,
} from "../../../src";
import {
canAcceptVerificationRequest,
ShowQrCodeCallbacks,
@@ -34,15 +42,20 @@ import {
VerifierEvent,
} from "../../../src/crypto-api/verification";
import { escapeRegExp } from "../../../src/utils";
import { CRYPTO_BACKENDS, emitPromise, InitCrypto } from "../../test-utils/test-utils";
import { CRYPTO_BACKENDS, emitPromise, getSyncResponse, InitCrypto, syncPromise } from "../../test-utils/test-utils";
import { SyncResponder } from "../../test-utils/SyncResponder";
import {
BOB_SIGNED_CROSS_SIGNING_KEYS_DATA,
BOB_SIGNED_TEST_DEVICE_DATA,
BOB_TEST_USER_ID,
MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64,
SIGNED_CROSS_SIGNING_KEYS_DATA,
SIGNED_TEST_DEVICE_DATA,
TEST_DEVICE_ID,
TEST_DEVICE_PUBLIC_ED25519_KEY_BASE64,
TEST_ROOM_ID,
TEST_USER_ID,
BOB_ONE_TIME_KEYS,
} from "../../test-utils/test-data";
import { mockInitialApiRequests } from "../../test-utils/mockEndpoints";
import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder";
@@ -808,6 +821,82 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("verification (%s)", (backend: st
});
});
describe("Send verification request in DM", () => {
beforeEach(async () => {
aliceClient = await startTestClient();
aliceClient.setGlobalErrorOnUnknownDevices(false);
e2eKeyResponder.addCrossSigningData(BOB_SIGNED_CROSS_SIGNING_KEYS_DATA);
e2eKeyResponder.addDeviceKeys(BOB_SIGNED_TEST_DEVICE_DATA);
syncResponder.sendOrQueueSyncResponse(getSyncResponse([BOB_TEST_USER_ID]));
// Wait for the sync response to be processed
await syncPromise(aliceClient);
});
/**
* Create a mock to respond when the verification request is sent
* Handle both encrypted and unencrypted requests
*/
function awaitRoomMessageRequest(): Promise<IContent> {
return new Promise((resolve) => {
// Case of unencrypted message of the new crypto
fetchMock.put(
"express:/_matrix/client/v3/rooms/:roomId/send/m.room.message/:txId",
(url: string, options: RequestInit) => {
resolve(JSON.parse(options.body as string));
return { event_id: "$YUwRidLecu:example.com" };
},
);
// Case of encrypted message of the old crypto
fetchMock.put(
"express:/_matrix/client/v3/rooms/:roomId/send/m.room.encrypted/:txId",
async (url: string, options: RequestInit) => {
const encryptedMessage = JSON.parse(options.body as string);
const event = new MatrixEvent({
content: encryptedMessage,
type: "m.room.encrypted",
room_id: TEST_ROOM_ID,
});
// Try to decrypt the event
event.once(MatrixEventEvent.Decrypted, (decryptedEvent: MatrixEvent, error?: Error) => {
expect(error).not.toBeDefined();
resolve(decryptedEvent.getContent());
});
await aliceClient.decryptEventIfNeeded(event);
return { event_id: "$YUwRidLecu:example.com" };
},
);
});
}
it("alice sends a verification request in a DM to bob", async () => {
fetchMock.post("express:/_matrix/client/v3/keys/claim", () => ({ one_time_keys: BOB_ONE_TIME_KEYS }));
// In `DeviceList#doQueuedQueries`, the key download response is processed every 5ms
// 5ms by users, ie Bob and Alice
await jest.advanceTimersByTimeAsync(10);
const messageRequestPromise = awaitRoomMessageRequest();
const verificationRequest = await aliceClient
.getCrypto()!
.requestVerificationDM(BOB_TEST_USER_ID, TEST_ROOM_ID);
const requestContent = await messageRequestPromise;
expect(requestContent.from_device).toBe(aliceClient.getDeviceId());
expect(requestContent.methods.sort()).toStrictEqual(
["m.sas.v1", "m.qr_code.scan.v1", "m.qr_code.show.v1", "m.reciprocate.v1"].sort(),
);
expect(requestContent.msgtype).toBe("m.key.verification.request");
expect(requestContent.to).toBe(BOB_TEST_USER_ID);
expect(verificationRequest.roomId).toBe(TEST_ROOM_ID);
expect(verificationRequest.isSelfVerification).toBe(false);
expect(verificationRequest.otherUserId).toBe(BOB_TEST_USER_ID);
});
});
async function startTestClient(opts: Partial<ICreateClientOpts> = {}): Promise<MatrixClient> {
const client = createClient({
baseUrl: TEST_HOMESERVER_URL,
@@ -1342,7 +1342,7 @@ describe("MatrixClient event timelines", function () {
function respondToContext(event: Partial<IEvent> = THREAD_ROOT): ExpectedHttpRequest {
const request = httpBackend.when(
"GET",
encodeUri("/_matrix/client/r0/rooms/$roomId/context/$eventId", {
encodeUri("/_matrix/client/v3/rooms/$roomId/context/$eventId", {
$roomId: roomId,
$eventId: event.event_id!,
}),
@@ -1360,7 +1360,7 @@ describe("MatrixClient event timelines", function () {
function respondToEvent(event: Partial<IEvent> = THREAD_ROOT): ExpectedHttpRequest {
const request = httpBackend.when(
"GET",
encodeUri("/_matrix/client/r0/rooms/$roomId/event/$eventId", {
encodeUri("/_matrix/client/v3/rooms/$roomId/event/$eventId", {
$roomId: roomId,
$eventId: event.event_id!,
}),
@@ -1371,7 +1371,7 @@ describe("MatrixClient event timelines", function () {
function respondToMessagesRequest(): ExpectedHttpRequest {
const request = httpBackend.when(
"GET",
encodeUri("/_matrix/client/r0/rooms/$roomId/messages", {
encodeUri("/_matrix/client/v3/rooms/$roomId/messages", {
$roomId: roomId,
}),
);
+79 -15
View File
@@ -73,7 +73,7 @@ describe("MatrixClient", function () {
it("should upload the file", function () {
httpBackend
.when("POST", "/_matrix/media/r0/upload")
.when("POST", "/_matrix/media/v3/upload")
.check(function (req) {
expect(req.rawData).toEqual(buf);
expect(req.queryParams?.filename).toEqual("hi.txt");
@@ -108,7 +108,7 @@ describe("MatrixClient", function () {
it("should parse errors into a MatrixError", function () {
httpBackend
.when("POST", "/_matrix/media/r0/upload")
.when("POST", "/_matrix/media/v3/upload")
.check(function (req) {
expect(req.rawData).toEqual(buf);
// @ts-ignore private property
@@ -708,7 +708,7 @@ describe("MatrixClient", function () {
const auth = { identifier: 1 };
it("should pass through an auth dict", function () {
httpBackend
.when("DELETE", "/_matrix/client/r0/devices/my_device")
.when("DELETE", "/_matrix/client/v3/devices/my_device")
.check(function (req) {
expect(req.data).toEqual({ auth: auth });
})
@@ -1102,10 +1102,6 @@ describe("MatrixClient", function () {
submit_url: "https://foobar.matrix/_matrix/matrix",
};
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.6.0"],
});
const prom = client.requestRegisterEmailToken("bob@email", "secret", 1);
httpBackend
.when("POST", "/register/email/requestToken")
@@ -1126,10 +1122,6 @@ describe("MatrixClient", function () {
it("should supply an id_access_token", async () => {
const targetEmail = "gerald@example.org";
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.6.0"],
});
httpBackend
.when("POST", "/invite")
.check((req) => {
@@ -1165,10 +1157,6 @@ describe("MatrixClient", function () {
],
};
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.6.0"],
});
httpBackend
.when("POST", "/createRoom")
.check((req) => {
@@ -1652,6 +1640,82 @@ describe("MatrixClient", function () {
]);
});
});
describe("getFallbackAuthUrl", () => {
it("should return fallback url", () => {
expect(client.getFallbackAuthUrl("loginType", "authSessionId")).toMatchInlineSnapshot(
`"http://alice.localhost.test.server/_matrix/client/v3/auth/loginType/fallback/web?session=authSessionId"`,
);
});
});
describe("addThreePidOnly", () => {
it("should make expected POST request", async () => {
httpBackend
.when("POST", "/_matrix/client/v3/account/3pid/add")
.check(function (req) {
expect(req.data).toEqual({
client_secret: "secret",
sid: "sid",
});
expect(req.headers["Authorization"]).toBe("Bearer " + accessToken);
})
.respond(200, {});
await Promise.all([
client.addThreePidOnly({
client_secret: "secret",
sid: "sid",
}),
httpBackend.flushAllExpected(),
]);
});
});
describe("bindThreePid", () => {
it("should make expected POST request", async () => {
httpBackend
.when("POST", "/_matrix/client/v3/account/3pid/bind")
.check(function (req) {
expect(req.data).toEqual({
client_secret: "secret",
id_server: "server",
id_access_token: "token",
sid: "sid",
});
expect(req.headers["Authorization"]).toBe("Bearer " + accessToken);
})
.respond(200, {});
await Promise.all([
client.bindThreePid({
client_secret: "secret",
id_server: "server",
id_access_token: "token",
sid: "sid",
}),
httpBackend.flushAllExpected(),
]);
});
});
describe("unbindThreePid", () => {
it("should make expected POST request", async () => {
httpBackend
.when("POST", "/_matrix/client/v3/account/3pid/unbind")
.check(function (req) {
expect(req.data).toEqual({
medium: "email",
address: "alice@server.com",
id_server: "identity.localhost",
});
expect(req.headers["Authorization"]).toBe("Bearer " + accessToken);
})
.respond(200, {});
await Promise.all([client.unbindThreePid("email", "alice@server.com"), httpBackend.flushAllExpected()]);
});
});
});
function withThreadId(event: MatrixEvent, newThreadId: string): MatrixEvent {
-4
View File
@@ -224,8 +224,6 @@ describe("MatrixClient syncing", () => {
});
it("should honour lazyLoadMembers if user is not a guest", () => {
client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true);
httpBackend!
.when("GET", "/sync")
.check((req) => {
@@ -242,8 +240,6 @@ describe("MatrixClient syncing", () => {
it("should not honour lazyLoadMembers if user is a guest", () => {
httpBackend!.expectedRequests = [];
httpBackend!.when("GET", "/versions").respond(200, {});
client!.doesServerSupportLazyLoading = jest.fn().mockResolvedValue(true);
httpBackend!
.when("GET", "/sync")
.check((req) => {
+1 -1
View File
@@ -121,7 +121,7 @@ describe("SlidingSyncSdk", () => {
await client!.initCrypto();
syncOpts.cryptoCallbacks = syncOpts.crypto = client!.crypto;
}
httpBackend!.when("GET", "/_matrix/client/r0/pushrules").respond(200, {});
httpBackend!.when("GET", "/_matrix/client/v3/pushrules").respond(200, {});
sdk = new SlidingSyncSdk(mockSlidingSync, client, testOpts, syncOpts);
};
-2
View File
@@ -75,8 +75,6 @@ export class E2EKeyReceiver implements IE2EKeyReceiver {
const listener = (url: string, options: RequestInit) =>
this.onKeyUploadRequest(resolveOneTimeKeys, options);
// catch both r0 and v3 variants
fetchMock.post(new URL("/_matrix/client/r0/keys/upload", homeserverUrl).toString(), listener);
fetchMock.post(new URL("/_matrix/client/v3/keys/upload", homeserverUrl).toString(), listener);
});
}
-2
View File
@@ -43,8 +43,6 @@ export class E2EKeyResponder {
public constructor(homeserverUrl: string) {
// set up a listener for /keys/query.
const listener = (url: string, options: RequestInit) => this.onKeyQueryRequest(options);
// catch both r0 and v3 variants
fetchMock.post(new URL("/_matrix/client/r0/keys/query", homeserverUrl).toString(), listener);
fetchMock.post(new URL("/_matrix/client/v3/keys/query", homeserverUrl).toString(), listener);
}
+1 -1
View File
@@ -75,7 +75,7 @@ export class SyncResponder implements ISyncResponder {
*/
public constructor(homeserverUrl: string) {
this.debug = debugFunc(`sync-responder:[${homeserverUrl}]`);
fetchMock.get("begin:" + new URL("/_matrix/client/r0/sync?", homeserverUrl).toString(), (_url, _options) =>
fetchMock.get("begin:" + new URL("/_matrix/client/v3/sync?", homeserverUrl).toString(), (_url, _options) =>
this.onSyncRequest(),
);
}
-1
View File
@@ -86,7 +86,6 @@ export const mockClientMethodsEvents = () => ({
* Returns basic mocked client methods related to server support
*/
export const mockClientMethodsServer = (): Partial<Record<MethodLikeKeys<MatrixClient>, unknown>> => ({
doesServerSupportSeparateAddAndBind: jest.fn(),
getIdentityServerUrl: jest.fn(),
getHomeserverUrl: jest.fn(),
getCapabilities: jest.fn().mockReturnValue({}),
+5 -5
View File
@@ -22,9 +22,9 @@ import fetchMock from "fetch-mock-jest";
* @param homeserverUrl - the homeserver url for the client under test
*/
export function mockInitialApiRequests(homeserverUrl: string) {
fetchMock.getOnce(new URL("/_matrix/client/versions", homeserverUrl).toString(), { versions: ["r0.5.0"] });
fetchMock.getOnce(new URL("/_matrix/client/r0/pushrules/", homeserverUrl).toString(), {});
fetchMock.postOnce(new URL("/_matrix/client/r0/user/%40alice%3Alocalhost/filter", homeserverUrl).toString(), {
fetchMock.getOnce(new URL("/_matrix/client/versions", homeserverUrl).toString(), { versions: ["v1.1"] });
fetchMock.getOnce(new URL("/_matrix/client/v3/pushrules/", homeserverUrl).toString(), {});
fetchMock.postOnce(new URL("/_matrix/client/v3/user/%40alice%3Alocalhost/filter", homeserverUrl).toString(), {
filter_id: "fid",
});
}
@@ -32,13 +32,13 @@ export function mockInitialApiRequests(homeserverUrl: string) {
/**
* Mock the requests needed to set up cross signing
*
* Return 404 error for `GET _matrix/client/r0/user/:userId/account_data/:type` request
* Return 404 error for `GET _matrix/client/v3/user/:userId/account_data/:type` request
* Return `{}` for `POST _matrix/client/v3/keys/signatures/upload` request (named `upload-sigs` for fetchMock check)
* Return `{}` for `POST /_matrix/client/(unstable|v3)/keys/device_signing/upload` request (named `upload-keys` for fetchMock check)
*/
export function mockSetupCrossSigningRequests(): void {
// have account_data requests return an empty object
fetchMock.get("express:/_matrix/client/r0/user/:userId/account_data/:type", {
fetchMock.get("express:/_matrix/client/v3/user/:userId/account_data/:type", {
status: 404,
body: { errcode: "M_NOT_FOUND", error: "Account data not found." },
});
+169 -67
View File
@@ -30,74 +30,122 @@ import json
from canonicaljson import encode_canonical_json
from cryptography.hazmat.primitives.asymmetric import ed25519, x25519
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from random import randbytes, seed
# input data
TEST_USER_ID = "@alice:localhost"
TEST_DEVICE_ID = "test_device"
TEST_ROOM_ID = "!room:id"
# any 32-byte string can be an ed25519 private key.
TEST_DEVICE_PRIVATE_KEY_BYTES = b"deadbeefdeadbeefdeadbeefdeadbeef"
ALICE_DATA = {
"TEST_USER_ID": "@alice:localhost",
"TEST_DEVICE_ID": "test_device",
"TEST_ROOM_ID": "!room:id",
# any 32-byte string can be an ed25519 private key.
"TEST_DEVICE_PRIVATE_KEY_BYTES": b"deadbeefdeadbeefdeadbeefdeadbeef",
MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES = b"doyouspeakwhaaaaaaaaaaaaaaaaaale"
USER_CROSS_SIGNING_PRIVATE_KEY_BYTES = b"useruseruseruseruseruseruseruser"
SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES = b"selfselfselfselfselfselfselfself"
"MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES": b"doyouspeakwhaaaaaaaaaaaaaaaaaale",
"USER_CROSS_SIGNING_PRIVATE_KEY_BYTES": b"useruseruseruseruseruseruseruser",
"SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES": b"selfselfselfselfselfselfselfself",
# Private key for secure key backup. There are some sessions encrypted with this key in megolm-backup.spec.ts
B64_BACKUP_DECRYPTION_KEY = "dwdtCnMYpX08FsFyUbJmRd9ML4frwJkqsXf7pR25LCo="
# Private key for secure key backup. There are some sessions encrypted with this key in megolm-backup.spec.ts
"B64_BACKUP_DECRYPTION_KEY": "dwdtCnMYpX08FsFyUbJmRd9ML4frwJkqsXf7pR25LCo=",
"OTK": "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw"
}
BOB_DATA = {
"TEST_USER_ID": "@bob:xyz",
"TEST_DEVICE_ID": "bob_device",
"TEST_ROOM_ID": "!room:id",
# any 32-byte string can be an ed25519 private key.
"TEST_DEVICE_PRIVATE_KEY_BYTES": b"Deadbeefdeadbeefdeadbeefdeadbeef",
"MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES": b"Doyouspeakwhaaaaaaaaaaaaaaaaaale",
"USER_CROSS_SIGNING_PRIVATE_KEY_BYTES": b"Useruseruseruseruseruseruseruser",
"SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES": b"Selfselfselfselfselfselfselfself",
# Private key for secure key backup. There are some sessions encrypted with this key in megolm-backup.spec.ts
"B64_BACKUP_DECRYPTION_KEY": "DwdtCnMYpX08FsFyUbJmRd9ML4frwJkqsXf7pR25LCo=",
"OTK": "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw"
}
def main() -> None:
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
TEST_DEVICE_PRIVATE_KEY_BYTES
print(
f"""\
/* Test data for cryptography tests
*
* Do not edit by hand! This file is generated by `./generate-test-data.py`
*/
import {{ IDeviceKeys, IMegolmSessionData }} from "../../../src/@types/crypto";
import {{ IDownloadKeyResult }} from "../../../src";
import {{ KeyBackupInfo }} from "../../../src/crypto-api";
/* eslint-disable comma-dangle */
// Alice data
{build_test_data(ALICE_DATA)}
// Bob data
{build_test_data(BOB_DATA, "BOB_")}
""",
end="",
)
# Use static seed to have stable random test data upon new generation
seed(10)
def build_test_data(user_data, prefix = "") -> str:
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
user_data["TEST_DEVICE_PRIVATE_KEY_BYTES"]
)
b64_public_key = encode_base64(
private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
)
device_data = {
"algorithms": ["m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"],
"device_id": TEST_DEVICE_ID,
"device_id": user_data["TEST_DEVICE_ID"],
"keys": {
f"curve25519:{TEST_DEVICE_ID}": "F4uCNNlcbRvc7CfBz95ZGWBvY1ALniG1J8+6rhVoKS0",
f"ed25519:{TEST_DEVICE_ID}": b64_public_key,
f"curve25519:{user_data['TEST_DEVICE_ID']}": "F4uCNNlcbRvc7CfBz95ZGWBvY1ALniG1J8+6rhVoKS0",
f"ed25519:{user_data['TEST_DEVICE_ID']}": b64_public_key,
},
"signatures": {TEST_USER_ID: {}},
"user_id": TEST_USER_ID,
"signatures": {user_data['TEST_USER_ID']: {}},
"user_id": user_data["TEST_USER_ID"],
}
device_data["signatures"][TEST_USER_ID][f"ed25519:{TEST_DEVICE_ID}"] = sign_json(
device_data["signatures"][user_data["TEST_USER_ID"]][f"ed25519:{user_data['TEST_DEVICE_ID']}"] = sign_json(
device_data, private_key
)
master_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES
user_data["MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES"]
)
b64_master_public_key = encode_base64(
master_private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
)
b64_master_private_key = encode_base64(MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES)
b64_master_private_key = encode_base64(user_data["MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES"])
self_signing_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES
user_data["SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES"]
)
b64_self_signing_public_key = encode_base64(
self_signing_private_key.public_key().public_bytes(
Encoding.Raw, PublicFormat.Raw
)
)
b64_self_signing_private_key = encode_base64(SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES)
b64_self_signing_private_key = encode_base64( user_data["SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES"])
user_signing_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
USER_CROSS_SIGNING_PRIVATE_KEY_BYTES
user_data["USER_CROSS_SIGNING_PRIVATE_KEY_BYTES"]
)
b64_user_signing_public_key = encode_base64(
user_signing_private_key.public_key().public_bytes(
Encoding.Raw, PublicFormat.Raw
)
)
b64_user_signing_private_key = encode_base64(USER_CROSS_SIGNING_PRIVATE_KEY_BYTES)
b64_user_signing_private_key = encode_base64(user_data["USER_CROSS_SIGNING_PRIVATE_KEY_BYTES"])
backup_decryption_key = x25519.X25519PrivateKey.from_private_bytes(
base64.b64decode(B64_BACKUP_DECRYPTION_KEY)
base64.b64decode(user_data["B64_BACKUP_DECRYPTION_KEY"])
)
b64_backup_public_key = encode_base64(
backup_decryption_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
@@ -113,75 +161,96 @@ def main() -> None:
# sign with our device key
sig = sign_json(backup_data["auth_data"], private_key)
backup_data["auth_data"]["signatures"] = {
TEST_USER_ID: {f"ed25519:{TEST_DEVICE_ID}": sig}
user_data["TEST_USER_ID"]: {f"ed25519:{user_data['TEST_DEVICE_ID']}": sig}
}
print(
f"""\
/* Test data for cryptography tests
*
* Do not edit by hand! This file is generated by `./generate-test-data.py`
*/
set_of_exported_room_keys = [build_exported_megolm_key(), build_exported_megolm_key()]
import {{ IDeviceKeys }} from "../../../src/@types/crypto";
import {{ IDownloadKeyResult }} from "../../../src";
import {{ KeyBackupInfo }} from "../../../src/crypto-api";
additional_exported_room_key = build_exported_megolm_key()
/* eslint-disable comma-dangle */
otk_to_sign = {
"key": user_data['OTK']
}
# sign our public otk key with our device key
otk = sign_json(otk_to_sign, private_key)
otks = {
user_data["TEST_USER_ID"]: {
user_data['TEST_DEVICE_ID']: {
"signed_curve25519:AAAAHQ": {
"key": user_data["OTK"],
"signatures": {
user_data["TEST_USER_ID"]: {f"ed25519:{user_data['TEST_DEVICE_ID']}": otk}
}
}
}
}
}
export const TEST_USER_ID = "{TEST_USER_ID}";
export const TEST_DEVICE_ID = "{TEST_DEVICE_ID}";
export const TEST_ROOM_ID = "{TEST_ROOM_ID}";
return f"""\
export const {prefix}TEST_USER_ID = "{user_data['TEST_USER_ID']}";
export const {prefix}TEST_DEVICE_ID = "{user_data['TEST_DEVICE_ID']}";
export const {prefix}TEST_ROOM_ID = "{user_data['TEST_ROOM_ID']}";
/** The base64-encoded public ed25519 key for this device */
export const TEST_DEVICE_PUBLIC_ED25519_KEY_BASE64 = "{b64_public_key}";
export const {prefix}TEST_DEVICE_PUBLIC_ED25519_KEY_BASE64 = "{b64_public_key}";
/** Signed device data, suitable for returning from a `/keys/query` call */
export const SIGNED_TEST_DEVICE_DATA: IDeviceKeys = {json.dumps(device_data, indent=4)};
export const {prefix}SIGNED_TEST_DEVICE_DATA: IDeviceKeys = {json.dumps(device_data, indent=4)};
/** base64-encoded public master cross-signing key */
export const MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_master_public_key}";
export const {prefix}MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_master_public_key}";
/** base64-encoded private master cross-signing key */
export const MASTER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_master_private_key}";
export const {prefix}MASTER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_master_private_key}";
/** base64-encoded public self cross-signing key */
export const SELF_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_self_signing_public_key}";
export const {prefix}SELF_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_self_signing_public_key}";
/** base64-encoded private self signing cross-signing key */
export const SELF_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_self_signing_private_key}";
export const {prefix}SELF_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_self_signing_private_key}";
/** base64-encoded public user cross-signing key */
export const USER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_user_signing_public_key}";
export const {prefix}USER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "{b64_user_signing_public_key}";
/** base64-encoded private user signing cross-signing key */
export const USER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_user_signing_private_key}";
export const {prefix}USER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "{b64_user_signing_private_key}";
/** Signed cross-signing keys data, also suitable for returning from a `/keys/query` call */
export const SIGNED_CROSS_SIGNING_KEYS_DATA: Partial<IDownloadKeyResult> = {
json.dumps(build_cross_signing_keys_data(), indent=4)
export const {prefix}SIGNED_CROSS_SIGNING_KEYS_DATA: Partial<IDownloadKeyResult> = {
json.dumps(build_cross_signing_keys_data(user_data), indent=4)
};
/** base64-encoded backup decryption (private) key */
export const BACKUP_DECRYPTION_KEY_BASE64 = "{ B64_BACKUP_DECRYPTION_KEY }";
export const {prefix}BACKUP_DECRYPTION_KEY_BASE64 = "{ user_data['B64_BACKUP_DECRYPTION_KEY'] }";
/** Signed backup data, suitable for return from `GET /_matrix/client/v3/room_keys/keys/{{roomId}}/{{sessionId}}` */
export const SIGNED_BACKUP_DATA: KeyBackupInfo = { json.dumps(backup_data, indent=4) };
""",
end="",
)
export const {prefix}SIGNED_BACKUP_DATA: KeyBackupInfo = { json.dumps(backup_data, indent=4) };
/** A set of megolm keys that can be imported via CryptoAPI#importRoomKeys */
export const {prefix}MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = {
json.dumps(set_of_exported_room_keys, indent=4)
};
/** An exported megolm session */
export const {prefix}MEGOLM_SESSION_DATA: IMegolmSessionData = {
json.dumps(additional_exported_room_key, indent=4)
};
/** Signed OTKs, returned by `POST /keys/claim` */
export const {prefix}ONE_TIME_KEYS = { json.dumps(otks, indent=4) };
"""
def build_cross_signing_keys_data() -> dict:
def build_cross_signing_keys_data(user_data) -> dict:
"""Build the signed cross-signing-keys data for return from /keys/query"""
master_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES
user_data["MASTER_CROSS_SIGNING_PRIVATE_KEY_BYTES"]
)
b64_master_public_key = encode_base64(
master_private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
)
self_signing_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES
user_data["SELF_CROSS_SIGNING_PRIVATE_KEY_BYTES"]
)
b64_self_signing_public_key = encode_base64(
self_signing_private_key.public_key().public_bytes(
@@ -189,7 +258,7 @@ def build_cross_signing_keys_data() -> dict:
)
)
user_signing_private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
USER_CROSS_SIGNING_PRIVATE_KEY_BYTES
user_data["USER_CROSS_SIGNING_PRIVATE_KEY_BYTES"]
)
b64_user_signing_public_key = encode_base64(
user_signing_private_key.public_key().public_bytes(
@@ -199,39 +268,39 @@ def build_cross_signing_keys_data() -> dict:
# create without signatures initially
cross_signing_keys_data = {
"master_keys": {
TEST_USER_ID: {
user_data["TEST_USER_ID"]: {
"keys": {
f"ed25519:{b64_master_public_key}": b64_master_public_key,
},
"user_id": TEST_USER_ID,
"user_id": user_data["TEST_USER_ID"],
"usage": ["master"],
}
},
"self_signing_keys": {
TEST_USER_ID: {
user_data["TEST_USER_ID"]: {
"keys": {
f"ed25519:{b64_self_signing_public_key}": b64_self_signing_public_key,
},
"user_id": TEST_USER_ID,
"user_id": user_data["TEST_USER_ID"],
"usage": ["self_signing"],
},
},
"user_signing_keys": {
TEST_USER_ID: {
user_data["TEST_USER_ID"]: {
"keys": {
f"ed25519:{b64_user_signing_public_key}": b64_user_signing_public_key,
},
"user_id": TEST_USER_ID,
"user_id": user_data["TEST_USER_ID"],
"usage": ["user_signing"],
},
},
}
# sign the sub-keys with the master
for k in ["self_signing_keys", "user_signing_keys"]:
to_sign = cross_signing_keys_data[k][TEST_USER_ID]
to_sign = cross_signing_keys_data[k][user_data["TEST_USER_ID"]]
sig = sign_json(to_sign, master_private_key)
to_sign["signatures"] = {
TEST_USER_ID: {f"ed25519:{b64_master_public_key}": sig}
user_data["TEST_USER_ID"]: {f"ed25519:{b64_master_public_key}": sig}
}
return cross_signing_keys_data
@@ -265,6 +334,39 @@ def sign_json(json_object: dict, private_key: ed25519.Ed25519PrivateKey) -> str:
return signature_base64
def build_exported_megolm_key() -> dict:
"""
Creates an exported megolm room key, as per https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-export-format
that can be imported via importRoomKeys API.
"""
index = 0
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(randbytes(32))
# Just use radom bytes for the ratchet parts
ratchet = randbytes(32 * 4)
# exported key, start with version byte
exported_key = bytearray(b'\x01')
exported_key += index.to_bytes(4, 'big')
exported_key += ratchet
# KPub
exported_key += private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
megolm_export = {
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!roomA:example.org",
"sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo",
"session_id": encode_base64(
private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
),
"session_key": encode_base64(exported_key),
"sender_claimed_keys": {
"ed25519": encode_base64(ed25519.Ed25519PrivateKey.from_private_bytes(randbytes(32)).public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)),
},
"forwarding_curve25519_key_chain": [],
}
return megolm_export
if __name__ == "__main__":
main()
+224 -1
View File
@@ -3,12 +3,14 @@
* Do not edit by hand! This file is generated by `./generate-test-data.py`
*/
import { IDeviceKeys } from "../../../src/@types/crypto";
import { IDeviceKeys, IMegolmSessionData } from "../../../src/@types/crypto";
import { IDownloadKeyResult } from "../../../src";
import { KeyBackupInfo } from "../../../src/crypto-api";
/* eslint-disable comma-dangle */
// Alice data
export const TEST_USER_ID = "@alice:localhost";
export const TEST_DEVICE_ID = "test_device";
export const TEST_ROOM_ID = "!room:id";
@@ -116,3 +118,224 @@ export const SIGNED_BACKUP_DATA: KeyBackupInfo = {
}
}
};
/** A set of megolm keys that can be imported via CryptoAPI#importRoomKeys */
export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [
{
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!roomA:example.org",
"sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo",
"session_id": "FYOoKQSwe4d9jhTZ/LQCZFJINjPEqZ7Or4Z08reP92M",
"session_key": "AQAAAABZ0jXQOprFfXe41tIFmAtHxflJp4O2hM/vzQQpOazOCFeWSoW5P3Z9Q+voU3eXehMwyP8/hm/Q8xLP6/PmJdy+71se/17kdFwcDGgLxBWfa4ODM9zlI4EjKbNqmiii5loJ7rBhA/XXaw80m0hfU6zTDX/KrO55J0Pt4vJ0LDa3LBWDqCkEsHuHfY4U2fy0AmRSSDYzxKmezq+GdPK3j/dj",
"sender_claimed_keys": {
"ed25519": "QdgHgdpDgihgovpPzUiThXur1fbErTFh7paFvNKSgN0"
},
"forwarding_curve25519_key_chain": []
},
{
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!roomA:example.org",
"sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo",
"session_id": "mPYSGA2l1tOQiipEDEVYhDSdTSFh2lDW1qpGKYZRxTc",
"session_key": "AQAAAAAHwgkB49BTPAEGTCK6degxUIbl8GPG2ugPRYhNtOpNic63u11+baXFfjDw5fmVfD1gJXpQQjGsqrIYioxrB1xzl7mfb942UHhYdaMQZowpp1fSpJVsxR5TddUU2EWifYD9EQsoz8mY1zqoazm4vUP4v9yxaTcUBj2c6HMJCY0gCJj2EhgNpdbTkIoqRAxFWIQ0nU0hYdpQ1taqRimGUcU3",
"sender_claimed_keys": {
"ed25519": "IrkbT6H+0urDf6wKDSyVC1fh1t84Vz6T62snni86Cog"
},
"forwarding_curve25519_key_chain": []
}
];
/** An exported megolm session */
export const MEGOLM_SESSION_DATA: IMegolmSessionData = {
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!roomA:example.org",
"sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo",
"session_id": "ipdI6Zs/7DzFTEhiA2iGaMDfHkIYCleqXT6L+5e1/co",
"session_key": "AQAAAABXGO+Z9jlQJhIL6ByhXrv2BwCIxkhh7MXpKLsYmXkJcWrQlirmXmD79ga1zo+I4DCtEZzyGSpDWXBC6G7ez3H4gDMBam1RE3Jm5tc+oTlIri32UkYgSL0kBkcEnttqmIXBlK8tAfJo3cJnlh7F4ltEOAqrdME6dU0zXTkqXmURqYqXSOmbP+w8xUxIYgNohmjA3x5CGApXql0+i/uXtf3K",
"sender_claimed_keys": {
"ed25519": "Bhbpt6hqMZlSH4sJV7xiEEEiPVeTWz4Vkujl1EMdIPI"
},
"forwarding_curve25519_key_chain": []
};
/** Signed OTKs, returned by `POST /keys/claim` */
export const ONE_TIME_KEYS = {
"@alice:localhost": {
"test_device": {
"signed_curve25519:AAAAHQ": {
"key": "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw",
"signatures": {
"@alice:localhost": {
"ed25519:test_device": "25djC6Rk6gIgFBMVawY9X9LnY8XMMziey6lKqL8Q5Bbp7T1vw9uk0RE7eKO2a/jNLcYroO2xRztGhBrKz5sOCQ"
}
}
}
}
}
};
// Bob data
export const BOB_TEST_USER_ID = "@bob:xyz";
export const BOB_TEST_DEVICE_ID = "bob_device";
export const BOB_TEST_ROOM_ID = "!room:id";
/** The base64-encoded public ed25519 key for this device */
export const BOB_TEST_DEVICE_PUBLIC_ED25519_KEY_BASE64 = "jmY0h8QS6Te6gxyjOmMc0eKOqmbAtXpVo4CCWFubk50";
/** Signed device data, suitable for returning from a `/keys/query` call */
export const BOB_SIGNED_TEST_DEVICE_DATA: IDeviceKeys = {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "bob_device",
"keys": {
"curve25519:bob_device": "F4uCNNlcbRvc7CfBz95ZGWBvY1ALniG1J8+6rhVoKS0",
"ed25519:bob_device": "jmY0h8QS6Te6gxyjOmMc0eKOqmbAtXpVo4CCWFubk50"
},
"user_id": "@bob:xyz",
"signatures": {
"@bob:xyz": {
"ed25519:bob_device": "4ApBs9jaeGyfdYaWRUdBvQAkDyXjACJ9KJ0xLHMgiFT/1yo6VqPTx2iziKGnrBiGhbtKNxEhDPOvZZkBU73cDQ"
}
}
};
/** base64-encoded public master cross-signing key */
export const BOB_MASTER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "KKVOHOB2LsW7hFJwqyzXpA+vp7u5+gaMWUJvBS7mjuA";
/** base64-encoded private master cross-signing key */
export const BOB_MASTER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "RG95b3VzcGVha3doYWFhYWFhYWFhYWFhYWFhYWFhbGU";
/** base64-encoded public self cross-signing key */
export const BOB_SELF_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "DaScI3WulBvDjf/d2vdyP5Cgjdypn1c/PSDX23MgN+A";
/** base64-encoded private self signing cross-signing key */
export const BOB_SELF_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "U2VsZnNlbGZzZWxmc2VsZnNlbGZzZWxmc2VsZnNlbGY";
/** base64-encoded public user cross-signing key */
export const BOB_USER_CROSS_SIGNING_PUBLIC_KEY_BASE64 = "lXP89FP6zvFH9TSbU1S8uSdAsVawm1NmV6z+Rfr3lEw";
/** base64-encoded private user signing cross-signing key */
export const BOB_USER_CROSS_SIGNING_PRIVATE_KEY_BASE64 = "VXNlcnVzZXJ1c2VydXNlcnVzZXJ1c2VydXNlcnVzZXI";
/** Signed cross-signing keys data, also suitable for returning from a `/keys/query` call */
export const BOB_SIGNED_CROSS_SIGNING_KEYS_DATA: Partial<IDownloadKeyResult> = {
"master_keys": {
"@bob:xyz": {
"keys": {
"ed25519:KKVOHOB2LsW7hFJwqyzXpA+vp7u5+gaMWUJvBS7mjuA": "KKVOHOB2LsW7hFJwqyzXpA+vp7u5+gaMWUJvBS7mjuA"
},
"user_id": "@bob:xyz",
"usage": [
"master"
]
}
},
"self_signing_keys": {
"@bob:xyz": {
"keys": {
"ed25519:DaScI3WulBvDjf/d2vdyP5Cgjdypn1c/PSDX23MgN+A": "DaScI3WulBvDjf/d2vdyP5Cgjdypn1c/PSDX23MgN+A"
},
"user_id": "@bob:xyz",
"usage": [
"self_signing"
],
"signatures": {
"@bob:xyz": {
"ed25519:KKVOHOB2LsW7hFJwqyzXpA+vp7u5+gaMWUJvBS7mjuA": "RxM8iJU6ZkyzQSVtNnXIJMPyEahVsN+fQQTBNKAs+kqySFyXBgchx+8czZaAhJCpXh9gD1nskT4yyFd2eyUXBw"
}
}
}
},
"user_signing_keys": {
"@bob:xyz": {
"keys": {
"ed25519:lXP89FP6zvFH9TSbU1S8uSdAsVawm1NmV6z+Rfr3lEw": "lXP89FP6zvFH9TSbU1S8uSdAsVawm1NmV6z+Rfr3lEw"
},
"user_id": "@bob:xyz",
"usage": [
"user_signing"
],
"signatures": {
"@bob:xyz": {
"ed25519:KKVOHOB2LsW7hFJwqyzXpA+vp7u5+gaMWUJvBS7mjuA": "jF8fvnPZulrPyh/4E8dNDVBP3iHHl9bRc+rRArVyGzoom+uVrokOck7BN2YmPyCRFZJJx7fgRA1Bveyu+mTVAg"
}
}
}
}
};
/** base64-encoded backup decryption (private) key */
export const BOB_BACKUP_DECRYPTION_KEY_BASE64 = "DwdtCnMYpX08FsFyUbJmRd9ML4frwJkqsXf7pR25LCo=";
/** Signed backup data, suitable for return from `GET /_matrix/client/v3/room_keys/keys/{roomId}/{sessionId}` */
export const BOB_SIGNED_BACKUP_DATA: KeyBackupInfo = {
"algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
"version": "1",
"auth_data": {
"public_key": "ZRuVWcWlDuvOwZRygccUCD4Avtnt130800I+WQNwwRY",
"signatures": {
"@bob:xyz": {
"ed25519:bob_device": "lDIMj3VC0WazE2FamGHpmbiqKf9Z4pO4qapZ5TL5BnD3c+dvb+2waOEd6pgay/pmrQ6MW4Eu2KDEpe1fnHc3BA"
}
}
}
};
/** A set of megolm keys that can be imported via CryptoAPI#importRoomKeys */
export const BOB_MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [
{
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!roomA:example.org",
"sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo",
"session_id": "X9SK5JceUdvUr9gZkmdfS78qmKztoL60oORJ9JA5XD8",
"session_key": "AQAAAADbfOdUj/ec5bK4xVEhhRw+jfd1FD4uA0MLy7NyVHugZxyXNZUx5YEof4H9YyjBretviZreMSXqflbgYKz257rkKu7MPeKFf7zmln2GxX0F/p++GOnvpY1FqOglhfRQi3tqiyOa7SL4f7TuERDTOpMqlWhIfTKQnqy0AyF2vpDi5V/UiuSXHlHb1K/YGZJnX0u/Kpis7aC+tKDkSfSQOVw/",
"sender_claimed_keys": {
"ed25519": "ZG6lrfATe+958wN1xaGf3dKG/CThEfkmNdp1jcu4zok"
},
"forwarding_curve25519_key_chain": []
},
{
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!roomA:example.org",
"sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo",
"session_id": "F4P7f1Z0RjbiZMgHk1xBCG3KC4/Ng9PmxLJ4hQ13sHA",
"session_key": "AQAAAACv0khqPrQ91MmWCgm0RTzfpn65AGCrRnAKLxGJdfSfECNZ8gyj34FZLwi+F+xC6ibFddcbLXW0mzR6PnTnHF3VHM4g/h+2rcxtlix8fySpIwFzaXViba7cOSy/b+dHTMZB40iA7F4y7AdTdHLv4N1XUj3puU/KVUIKf9/lEDLqyReD+39WdEY24mTIB5NcQQhtyguPzYPT5sSyeIUNd7Bw",
"sender_claimed_keys": {
"ed25519": "HxUKnGfeUu0fF3cLyCFSDXYtVCQHy/+33q9RkzKfsiU"
},
"forwarding_curve25519_key_chain": []
}
];
/** An exported megolm session */
export const BOB_MEGOLM_SESSION_DATA: IMegolmSessionData = {
"algorithm": "m.megolm.v1.aes-sha2",
"room_id": "!roomA:example.org",
"sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo",
"session_id": "OsZMdC1gQ5nPr+L9tuT6xXsaFJkVPkgxP2FexHF1/QM",
"session_key": "AQAAAACvcoGk7mOY59fOqZaxFUiTCBRV1Ia94KBjAZx6kgdgBtkkvs50z8od8/Nc9ncK2UsEiXNvCTTp2dlN3du+Rx0/m7vet2ZOEEp2oYDjHMLLFmwd1gtlGuWYPdXA6Y1+9Yyph0/EDVfS+zd3XvbL0QgbyL43+yQnFNHKlxVJX1eiKTrGTHQtYEOZz6/i/bbk+sV7GhSZFT5IMT9hXsRxdf0D",
"sender_claimed_keys": {
"ed25519": "dV0TIhhkToXpL+gZLo+zXDHJfw7MWYxpg80cynIQDv0"
},
"forwarding_curve25519_key_chain": []
};
/** Signed OTKs, returned by `POST /keys/claim` */
export const BOB_ONE_TIME_KEYS = {
"@bob:xyz": {
"bob_device": {
"signed_curve25519:AAAAHQ": {
"key": "j3fR3HemM16M7CWhoI4Sk5ZsdmdfQHsKL1xuSft6MSw",
"signatures": {
"@bob:xyz": {
"ed25519:bob_device": "dlZc9VA/hP980Mxvu9qwi0qJx8VK7sADGOM48CE01YM7K/Mbty9lis/QjtQAWqDg371QyynVRjEzt9qj7eSFCg"
}
}
}
}
}
};
+43 -12
View File
@@ -18,7 +18,7 @@ limitations under the License.
import fetchMock from "fetch-mock-jest";
import MockHttpBackend from "matrix-mock-request";
import { M_AUTHENTICATION } from "../../src";
import { AutoDiscoveryAction, M_AUTHENTICATION } from "../../src";
import { AutoDiscovery } from "../../src/autodiscovery";
import { OidcError } from "../../src/oidc/error";
import { makeDelegatedAuthConfig } from "../test-utils/oidc";
@@ -351,7 +351,7 @@ describe("AutoDiscovery", function () {
function () {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
not_matrix_versions: ["r0.0.1"],
not_matrix_versions: ["v1.1"],
});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {
@@ -388,7 +388,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {
@@ -428,7 +428,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {
@@ -469,7 +469,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {
@@ -515,7 +515,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {
@@ -560,7 +560,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {
@@ -606,7 +606,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend.when("GET", "/_matrix/identity/v2").respond(404, {});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
@@ -653,7 +653,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend.when("GET", "/_matrix/identity/v2").respond(500, {});
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
@@ -697,7 +697,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend
.when("GET", "/_matrix/identity/v2")
@@ -747,7 +747,7 @@ describe("AutoDiscovery", function () {
expect(req.path).toEqual("https://chat.example.org/_matrix/client/versions");
})
.respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
httpBackend
.when("GET", "/_matrix/identity/v2")
@@ -867,6 +867,37 @@ describe("AutoDiscovery", function () {
]);
});
it("should FAIL_ERROR for unsupported Matrix version", () => {
const httpBackend = getHttpBackend();
httpBackend.when("GET", "/.well-known/matrix/client").respond(200, {
"m.homeserver": {
base_url: "https://example.org",
},
});
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.6.0"],
});
return Promise.all([
httpBackend.flushAllExpected(),
AutoDiscovery.findClientConfig("example.org").then((conf) => {
const expected = {
"m.homeserver": {
state: AutoDiscoveryAction.FAIL_ERROR,
error: AutoDiscovery.ERROR_HOMESERVER_TOO_OLD,
base_url: "https://example.org",
},
"m.identity_server": {
state: "PROMPT",
error: null,
base_url: null,
},
};
expect(conf).toEqual(expected);
}),
]);
});
describe("m.authentication", () => {
const homeserverName = "example.org";
const homeserverUrl = "https://chat.example.org/";
@@ -879,7 +910,7 @@ describe("AutoDiscovery", function () {
beforeEach(() => {
fetchMock.resetBehavior();
fetchMock.get(`${homeserverUrl}_matrix/client/versions`, { versions: ["r0.0.1"] });
fetchMock.get(`${homeserverUrl}_matrix/client/versions`, { versions: ["v1.1"] });
fetchMock.get("https://example.org/.well-known/matrix/client", {
"m.homeserver": {
+4 -4
View File
@@ -33,7 +33,7 @@ describe("ContentRepo", function () {
it("should return a download URL if no width/height/resize are specified", function () {
const mxcUri = "mxc://server.name/resourceid";
expect(getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
baseUrl + "/_matrix/media/r0/download/server.name/resourceid",
baseUrl + "/_matrix/media/v3/download/server.name/resourceid",
);
});
@@ -44,21 +44,21 @@ describe("ContentRepo", function () {
it("should return a thumbnail URL if a width/height/resize is specified", function () {
const mxcUri = "mxc://server.name/resourceid";
expect(getHttpUriForMxc(baseUrl, mxcUri, 32, 64, "crop")).toEqual(
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" + "?width=32&height=64&method=crop",
baseUrl + "/_matrix/media/v3/thumbnail/server.name/resourceid" + "?width=32&height=64&method=crop",
);
});
it("should put fragments from mxc:// URIs after any query parameters", function () {
const mxcUri = "mxc://server.name/resourceid#automade";
expect(getHttpUriForMxc(baseUrl, mxcUri, 32)).toEqual(
baseUrl + "/_matrix/media/r0/thumbnail/server.name/resourceid" + "?width=32#automade",
baseUrl + "/_matrix/media/v3/thumbnail/server.name/resourceid" + "?width=32#automade",
);
});
it("should put fragments from mxc:// URIs at the end of the HTTP URI", function () {
const mxcUri = "mxc://server.name/resourceid#automade";
expect(getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
baseUrl + "/_matrix/media/r0/download/server.name/resourceid#automade",
baseUrl + "/_matrix/media/v3/download/server.name/resourceid#automade",
);
});
});
@@ -6,6 +6,6 @@ exports[`MatrixHttpApi should return expected object from \`getContentUri\` 1`]
"params": {
"access_token": "token",
},
"path": "/_matrix/media/r0/upload",
"path": "/_matrix/media/v3/upload",
}
`;
+4 -4
View File
@@ -84,7 +84,7 @@ describe("MatrixHttpApi", () => {
upload = api.uploadContent({} as File);
expect(xhr.open).toHaveBeenCalledWith(
Method.Post,
baseUrl.toLowerCase() + "/_matrix/media/r0/upload?access_token=token",
baseUrl.toLowerCase() + "/_matrix/media/v3/upload?access_token=token",
);
expect(xhr.setRequestHeader).not.toHaveBeenCalledWith("Authorization");
});
@@ -96,7 +96,7 @@ describe("MatrixHttpApi", () => {
accessToken: "token",
});
upload = api.uploadContent({} as File);
expect(xhr.open).toHaveBeenCalledWith(Method.Post, baseUrl.toLowerCase() + "/_matrix/media/r0/upload");
expect(xhr.open).toHaveBeenCalledWith(Method.Post, baseUrl.toLowerCase() + "/_matrix/media/v3/upload");
expect(xhr.setRequestHeader).toHaveBeenCalledWith("Authorization", "Bearer token");
});
@@ -105,14 +105,14 @@ describe("MatrixHttpApi", () => {
upload = api.uploadContent({} as File, { name: "name" });
expect(xhr.open).toHaveBeenCalledWith(
Method.Post,
baseUrl.toLowerCase() + "/_matrix/media/r0/upload?filename=name",
baseUrl.toLowerCase() + "/_matrix/media/v3/upload?filename=name",
);
});
it("should allow not sending the filename", () => {
const api = new MatrixHttpApi(new TypedEventEmitter<any, any>(), { baseUrl, prefix });
upload = api.uploadContent({} as File, { name: "name", includeFilename: false });
expect(xhr.open).toHaveBeenCalledWith(Method.Post, baseUrl.toLowerCase() + "/_matrix/media/r0/upload");
expect(xhr.open).toHaveBeenCalledWith(Method.Post, baseUrl.toLowerCase() + "/_matrix/media/v3/upload");
});
it("should abort xhr when the upload is aborted", () => {
+1 -1
View File
@@ -201,7 +201,7 @@ describe("MatrixClient", function () {
if (path === KEEP_ALIVE_PATH && acceptKeepalives) {
return Promise.resolve({
unstable_features: unstableFeatures,
versions: ["r0.6.0", "r0.6.1"],
versions: ["v1.1"],
});
}
const next = httpLookups.shift();
+3 -3
View File
@@ -195,7 +195,7 @@ describe.each([[StoreType.Memory], [StoreType.IndexedDB]])("queueToDevice (%s st
it("retries on retryImmediately()", async function () {
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
await Promise.all([client.startClient(), httpBackend.flush(undefined, 1, 20)]);
@@ -219,7 +219,7 @@ describe.each([[StoreType.Memory], [StoreType.IndexedDB]])("queueToDevice (%s st
it("retries on when client is started", async function () {
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
await Promise.all([client.startClient(), httpBackend.flush("/_matrix/client/versions", 1, 20)]);
@@ -243,7 +243,7 @@ describe.each([[StoreType.Memory], [StoreType.IndexedDB]])("queueToDevice (%s st
it("retries when a message is retried", async function () {
httpBackend.when("GET", "/_matrix/client/versions").respond(200, {
versions: ["r0.0.1"],
versions: ["v1.1"],
});
await Promise.all([client.startClient(), httpBackend.flush(undefined, 1, 20)]);
@@ -161,7 +161,7 @@ describe("OutgoingRequestProcessor", () => {
.when("PUT", "/_matrix")
.check((req) => {
expect(req.path).toEqual(
"https://example.com/_matrix/client/v3/room/test%2Froom/send/test%2Ftype/test%2Ftxnid",
"https://example.com/_matrix/client/v3/rooms/test%2Froom/send/test%2Ftype/test%2Ftxnid",
);
expect(req.rawData).toEqual(testBody);
expect(req.headers["Accept"]).toEqual("application/json");
+12 -28
View File
@@ -61,32 +61,7 @@ describe("RustCrypto", () => {
);
it("should import and export keys", async () => {
const someRoomKeys = [
{
algorithm: "m.megolm.v1.aes-sha2",
room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480",
sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY",
session_id: "iGQG5GaP1/B3dSH6zCQDQqrNuotrtQjVC7w1OsUDwbg",
session_key:
"AQAAAADaCbP2gdOy8jrhikjploKgSBaFSJ5rvHcziaADbwNEzeCSrfuAUlXvCvxik8kU+MfCHIi5arN2M7UM5rGKdzkHnkReoIByFkeMdbjKWk5SFpVQexcM74eDhBGj+ICkQqOgApfnEbSswrmreB0+MhHHyLStwW5fy5f8A9QW1sbPuohkBuRmj9fwd3Uh+swkA0KqzbqLa7UI1Qu8NTrFA8G4",
sender_claimed_keys: {
ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k",
},
forwarding_curve25519_key_chain: [],
},
{
algorithm: "m.megolm.v1.aes-sha2",
room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480",
sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY",
session_id: "P/Jy9Tog4CMtLseeS4Fe2AEXZov3k6cibcop/uyhr78",
session_key:
"AQAAAAATyAVm0c9c9DW9Od72MxvfSDYoysBw3C6yMJ3bYuTmssHN7yNGm59KCtKeFp2Y5qO7lvUmwOfSTvTASUb7HViE7Lt+Bvp5WiMTJ2Pv6m+N12ihyowV5lgtKFWI18Wxd0AugMTVQRwjBK6aMobf86NXWD2hiKm3N6kWbC0PXmqV7T/ycvU6IOAjLS7HnkuBXtgBF2aL95OnIm3KKf7soa+/",
sender_claimed_keys: {
ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k",
},
forwarding_curve25519_key_chain: [],
},
];
const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY;
let importTotal = 0;
const opt: ImportRoomKeysOpts = {
progressCallback: (stage) => {
@@ -95,11 +70,11 @@ describe("RustCrypto", () => {
};
await rustCrypto.importRoomKeys(someRoomKeys, opt);
expect(importTotal).toBe(2);
expect(importTotal).toBe(someRoomKeys.length);
const keys = await rustCrypto.exportRoomKeys();
expect(Array.isArray(keys)).toBeTruthy();
expect(keys.length).toBe(2);
expect(keys.length).toBe(someRoomKeys.length);
const aSession = someRoomKeys[0];
@@ -585,6 +560,15 @@ describe("RustCrypto", () => {
);
});
});
describe("requestVerificationDM", () => {
it("send verification request to an unknown user", async () => {
const rustCrypto = await makeTestRustCrypto();
await expect(() =>
rustCrypto.requestVerificationDM("@bob:example.com", testData.TEST_ROOM_ID),
).rejects.toThrow("unknown userId @bob:example.com");
});
});
});
/** build a basic RustCrypto instance for testing
+36 -1
View File
@@ -17,8 +17,9 @@ limitations under the License.
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
import { Mocked } from "jest-mock";
import { RustVerificationRequest } from "../../../src/rust-crypto/verification";
import { isVerificationEvent, RustVerificationRequest } from "../../../src/rust-crypto/verification";
import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor";
import { EventType, MatrixEvent, MsgType } from "../../../src";
describe("VerificationRequest", () => {
describe("pending", () => {
@@ -80,6 +81,40 @@ describe("VerificationRequest", () => {
});
});
describe("isVerificationEvent", () => {
it.each([
[EventType.KeyVerificationCancel],
[EventType.KeyVerificationDone],
[EventType.KeyVerificationMac],
[EventType.KeyVerificationStart],
[EventType.KeyVerificationKey],
[EventType.KeyVerificationReady],
[EventType.KeyVerificationAccept],
])("should return true with %s event", (eventType) => {
const event = new MatrixEvent({
type: eventType,
});
expect(isVerificationEvent(event)).toBe(true);
});
it("should return true with EventType.RoomMessage and MsgType.KeyVerificationRequest", () => {
const event = new MatrixEvent({
type: EventType.RoomMessage,
content: {
msgtype: MsgType.KeyVerificationRequest,
},
});
expect(isVerificationEvent(event)).toBe(true);
});
it("should return false with a non verification event", () => {
const event = new MatrixEvent({
type: EventType.RoomName,
});
expect(isVerificationEvent(event)).toBe(false);
});
});
/** build a RustVerificationRequest with default parameters */
function makeTestRequest(
inner?: RustSdkCryptoJs.VerificationRequest,
-6
View File
@@ -193,12 +193,6 @@ export interface IBindThreePidBody {
sid: string;
}
export interface IAddThreePidBody {
client_secret: string;
id_server: string;
sid: string;
}
export interface IRelationsRequestOpts {
from?: string;
to?: string;
+19 -1
View File
@@ -28,6 +28,7 @@ import {
validateWellKnownAuthentication,
} from "./oidc/validate";
import { OidcError } from "./oidc/error";
import { MINIMUM_MATRIX_VERSION } from "./version-support";
// Dev note: Auto discovery is part of the spec.
// See: https://matrix.org/docs/spec/client_server/r0.4.0.html#server-discovery
@@ -50,6 +51,9 @@ enum AutoDiscoveryError {
InvalidIs = "Invalid identity server discovery response",
MissingWellknown = "No .well-known JSON file found",
InvalidJson = "Invalid JSON",
HomeserverTooOld = "The homeserver does not meet the minimum version requirements",
// TODO: Implement when Sydent supports the `/versions` endpoint - https://github.com/matrix-org/sydent/issues/424
//IdentityServerTooOld = "The identity server does not meet the minimum version requirements",
}
interface AutoDiscoveryState {
@@ -108,6 +112,8 @@ export class AutoDiscovery {
public static readonly ERROR_INVALID_JSON = AutoDiscoveryError.InvalidJson;
public static readonly ERROR_HOMESERVER_TOO_OLD = AutoDiscoveryError.HomeserverTooOld;
public static readonly ALL_ERRORS = Object.keys(AutoDiscoveryError);
/**
@@ -199,7 +205,7 @@ export class AutoDiscovery {
// Step 3: Make sure the homeserver URL points to a homeserver.
const hsVersions = await this.fetchWellKnownObject<IServerVersions>(`${hsUrl}/_matrix/client/versions`);
if (!hsVersions?.raw?.["versions"]) {
if (!hsVersions || !Array.isArray(hsVersions.raw?.["versions"])) {
logger.error("Invalid /versions response");
clientConfig["m.homeserver"].error = AutoDiscovery.ERROR_INVALID_HOMESERVER;
@@ -210,6 +216,18 @@ export class AutoDiscovery {
return Promise.resolve(clientConfig);
}
// Step 3.1: Non-spec check to ensure the server will actually work for us
if (!hsVersions.raw!["versions"].includes(MINIMUM_MATRIX_VERSION)) {
logger.error("Homeserver does not meet minimum version requirements");
clientConfig["m.homeserver"].error = AutoDiscovery.ERROR_HOMESERVER_TOO_OLD;
// Supply the base_url to the caller because they may be ignoring liveliness
// errors, like this one.
clientConfig["m.homeserver"].base_url = hsUrl;
return Promise.resolve(clientConfig);
}
// Step 4: Now that the homeserver looks valid, update our client config.
clientConfig["m.homeserver"] = {
state: AutoDiscovery.SUCCESS,
+24 -185
View File
@@ -133,7 +133,6 @@ import {
IFilterResponse,
ITagsResponse,
IStatusResponse,
IAddThreePidBody,
KnockRoomOpts,
} from "./@types/requests";
import {
@@ -1287,7 +1286,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
baseUrl: opts.baseUrl,
idBaseUrl: opts.idBaseUrl,
accessToken: opts.accessToken,
prefix: ClientPrefix.R0,
prefix: ClientPrefix.V3,
onlyData: true,
extraParams: opts.queryParams,
localTimeoutMs: opts.localTimeoutMs,
@@ -2252,6 +2251,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
this.reEmitter.reEmit(rustCrypto, [
CryptoEvent.VerificationRequestReceived,
CryptoEvent.UserTrustStatusChanged,
CryptoEvent.KeyBackupStatus,
CryptoEvent.KeyBackupSessionsRemaining,
CryptoEvent.KeyBackupFailed,
]);
}
@@ -2442,6 +2444,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*
* @returns resolves to a VerificationRequest
* when the request has been sent to the other party.
*
* @deprecated Prefer {@link CryptoApi.requestVerificationDM}.
*/
public requestVerificationDM(userId: string, roomId: string): Promise<VerificationRequest> {
if (!this.crypto) {
@@ -3251,6 +3255,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* getKeyBackupVersion) in backupInfo and
* trust information (as returned by isKeyBackupTrusted)
* in trustInfo.
*
* @deprecated Prefer {@link CryptoApi.checkKeyBackupAndEnable}.
*/
public checkKeyBackup(): Promise<IKeyBackupCheck | null> {
if (!this.crypto) {
@@ -3320,6 +3326,8 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*
* @param info - Backup information object as returned by getKeyBackupVersion
* @returns Promise which resolves when complete.
*
* @deprecated Do not call this directly. Instead call {@link CryptoApi.checkKeyBackupAndEnable}.
*/
public enableKeyBackup(info: IKeyBackupInfo): Promise<void> {
if (!this.crypto) {
@@ -3425,9 +3433,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
await this.crypto.crossSigningInfo.signObject(data.auth_data, "master");
}
const res = await this.http.authedRequest<IKeyBackupInfo>(Method.Post, "/room_keys/version", undefined, data, {
prefix: ClientPrefix.V3,
});
const res = await this.http.authedRequest<IKeyBackupInfo>(Method.Post, "/room_keys/version", undefined, data);
// We could assume everything's okay and enable directly, but this ensures
// we run the same signature verification that will be used for future
@@ -3924,7 +3930,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*/
public getMediaConfig(): Promise<IMediaConfig> {
return this.http.authedRequest(Method.Get, "/config", undefined, undefined, {
prefix: MediaPrefix.R0,
prefix: MediaPrefix.V3,
});
}
@@ -5179,7 +5185,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
},
undefined,
{
prefix: MediaPrefix.R0,
prefix: MediaPrefix.V3,
priority: "low",
},
);
@@ -5346,7 +5352,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
address: address,
};
if (this.identityServer?.getAccessToken && (await this.doesServerAcceptIdentityAccessToken())) {
if (this.identityServer?.getAccessToken) {
const identityAccessToken = await this.identityServer.getAccessToken();
if (identityAccessToken) {
params["id_access_token"] = identityAccessToken;
@@ -6138,7 +6144,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
const opts = {
prefix:
Thread.hasServerSideListSupport === FeatureSupport.Stable
? "/_matrix/client/v1"
? ClientPrefix.V1
: "/_matrix/client/unstable/org.matrix.msc3856",
};
@@ -6664,20 +6670,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
): Promise<T> {
const postParams = Object.assign({}, params);
// If the HS supports separate add and bind, then requestToken endpoints
// don't need an IS as they are all validated by the HS directly.
if (!(await this.doesServerSupportSeparateAddAndBind()) && this.idBaseUrl) {
const idServerUrl = new URL(this.idBaseUrl);
postParams.id_server = idServerUrl.host;
if (this.identityServer?.getAccessToken && (await this.doesServerAcceptIdentityAccessToken())) {
const identityAccessToken = await this.identityServer.getAccessToken();
if (identityAccessToken) {
postParams.id_access_token = identityAccessToken;
}
}
}
return this.http.request(Method.Post, endpoint, undefined, postParams);
}
@@ -7382,78 +7374,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return versions && versions.includes(version);
}
/**
* Query the server to see if it supports members lazy loading
* @returns true if server supports lazy loading
*/
public async doesServerSupportLazyLoading(): Promise<boolean> {
const response = await this.getVersions();
if (!response) return false;
const versions = response["versions"];
const unstableFeatures = response["unstable_features"];
return (
(versions && versions.includes("r0.5.0")) || (unstableFeatures && unstableFeatures["m.lazy_load_members"])
);
}
/**
* Query the server to see if the `id_server` parameter is required
* when registering with an 3pid, adding a 3pid or resetting password.
* @returns true if id_server parameter is required
*/
public async doesServerRequireIdServerParam(): Promise<boolean> {
const response = await this.getVersions();
if (!response) return true;
const versions = response["versions"];
// Supporting r0.6.0 is the same as having the flag set to false
if (versions && versions.includes("r0.6.0")) {
return false;
}
const unstableFeatures = response["unstable_features"];
if (!unstableFeatures) return true;
if (unstableFeatures["m.require_identity_server"] === undefined) {
return true;
} else {
return unstableFeatures["m.require_identity_server"];
}
}
/**
* Query the server to see if the `id_access_token` parameter can be safely
* passed to the homeserver. Some homeservers may trigger errors if they are not
* prepared for the new parameter.
* @returns true if id_access_token can be sent
*/
public async doesServerAcceptIdentityAccessToken(): Promise<boolean> {
const response = await this.getVersions();
if (!response) return false;
const versions = response["versions"];
const unstableFeatures = response["unstable_features"];
return (versions && versions.includes("r0.6.0")) || (unstableFeatures && unstableFeatures["m.id_access_token"]);
}
/**
* Query the server to see if it supports separate 3PID add and bind functions.
* This affects the sequence of API calls clients should use for these operations,
* so it's helpful to be able to check for support.
* @returns true if separate functions are supported
*/
public async doesServerSupportSeparateAddAndBind(): Promise<boolean> {
const response = await this.getVersions();
if (!response) return false;
const versions = response["versions"];
const unstableFeatures = response["unstable_features"];
return versions?.includes("r0.6.0") || unstableFeatures?.["m.separate_add_and_bind"];
}
/**
* Query the server to see if it lists support for an unstable feature
* in the /versions response
@@ -7525,14 +7445,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
}
}
/**
* Query the server to see if it supports the MSC2457 `logout_devices` parameter when setting password
* @returns true if server supports the `logout_devices` parameter
*/
public doesServerSupportLogoutDevices(): Promise<boolean> {
return this.isVersionSupported("r0.6.1");
}
/**
* Get if lazy loading members is being used.
* @returns Whether or not members are lazy loaded by this client
@@ -7917,18 +7829,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
});
}
/**
* @param relayState - URL Callback after SAML2 Authentication
* @returns Promise which resolves to a LoginResponse object
* @returns Rejects: with an error response.
* @deprecated this isn't in the Matrix spec anymore
*/
public loginWithSAML2(relayState: string): Promise<LoginResponse> {
return this.login("m.login.saml2", {
relay_state: relayState,
});
}
/**
* @param redirectUrl - The URL to redirect to after the HS
* authenticates with CAS.
@@ -7958,7 +7858,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
[SSO_ACTION_PARAM.unstable!]: action,
};
return this.http.getUrl(url, params, ClientPrefix.R0).href;
return this.http.getUrl(url, params).href;
}
/**
@@ -8076,13 +7976,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
$loginType: loginType,
});
return this.http.getUrl(
path,
{
session: authSessionId,
},
ClientPrefix.R0,
).href;
return this.http.getUrl(path, {
session: authSessionId,
}).href;
}
/**
@@ -8097,11 +7993,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
// inject the id_access_token if inviting 3rd party addresses
const invitesNeedingToken = (options.invite_3pid || []).filter((i) => !i.id_access_token);
if (
invitesNeedingToken.length > 0 &&
this.identityServer?.getAccessToken &&
(await this.doesServerAcceptIdentityAccessToken())
) {
if (invitesNeedingToken.length > 0 && this.identityServer?.getAccessToken) {
const identityAccessToken = await this.identityServer.getAccessToken();
if (identityAccessToken) {
for (const invite of invitesNeedingToken) {
@@ -8460,30 +8352,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return this.http.authedRequest(Method.Put, path, undefined, { visibility });
}
/**
* Set the visbility of a room bridged to a 3rd party network in
* the current HS's room directory.
* @param networkId - the network ID of the 3rd party
* instance under which this room is published under.
* @param visibility - "public" to make the room visible
* in the public directory, or "private" to make
* it invisible.
* @returns Promise which resolves: result object
* @returns Rejects: with an error response.
* @deprecated missing from the spec
*/
public setRoomDirectoryVisibilityAppService(
networkId: string,
roomId: string,
visibility: "public" | "private",
): Promise<any> {
const path = utils.encodeUri("/directory/list/appservice/$networkId/$roomId", {
$networkId: networkId,
$roomId: roomId,
});
return this.http.authedRequest(Method.Put, path, undefined, { visibility: visibility });
}
/**
* Query the user directory with a term matching user IDs, display names and domains.
* @param term - the term with which to search.
@@ -8566,31 +8434,10 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
return this.http.authedRequest(Method.Get, "/account/3pid");
}
/**
* Add a 3PID to your homeserver account and optionally bind it to an identity
* server as well. An identity server is required as part of the `creds` object.
*
* @deprecated this API is deprecated, and you should instead use `addThreePidOnly` for homeservers that support it.
*
* @returns Promise which resolves: on success
* @returns Rejects: with an error response.
*/
public addThreePid(creds: IAddThreePidBody, bind: boolean): Promise<{ submit_url?: string }> {
const path = "/account/3pid";
const data = {
threePidCreds: creds,
bind: bind,
};
return this.http.authedRequest(Method.Post, path, undefined, data);
}
/**
* Add a 3PID to your homeserver account. This API does not use an identity
* server, as the homeserver is expected to handle 3PID ownership validation.
*
* You can check whether a homeserver supports this API via
* `doesServerSupportSeparateAddAndBind`.
*
* @param data - A object with 3PID validation data from having called
* `account/3pid/<medium>/requestToken` on the homeserver.
* @returns Promise which resolves: to an empty object `{}`
@@ -8598,8 +8445,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*/
public async addThreePidOnly(data: IAddThreePidOnlyBody): Promise<{}> {
const path = "/account/3pid/add";
const prefix = (await this.isVersionSupported("r0.6.0")) ? ClientPrefix.R0 : ClientPrefix.Unstable;
return this.http.authedRequest(Method.Post, path, undefined, data, { prefix });
return this.http.authedRequest(Method.Post, path, undefined, data);
}
/**
@@ -8607,9 +8453,6 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
* identity server handles 3PID ownership validation and the homeserver records
* the new binding to track where all 3PIDs for the account are bound.
*
* You can check whether a homeserver supports this API via
* `doesServerSupportSeparateAddAndBind`.
*
* @param data - A object with 3PID validation data from having called
* `validate/<medium>/requestToken` on the identity server. It should also
* contain `id_server` and `id_access_token` fields as well.
@@ -8618,8 +8461,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
*/
public async bindThreePid(data: IBindThreePidBody): Promise<{}> {
const path = "/account/3pid/bind";
const prefix = (await this.isVersionSupported("r0.6.0")) ? ClientPrefix.R0 : ClientPrefix.Unstable;
return this.http.authedRequest(Method.Post, path, undefined, data, { prefix });
return this.http.authedRequest(Method.Post, path, undefined, data);
}
/**
@@ -8644,8 +8486,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
address,
id_server: this.getIdentityServerUrl(true),
};
const prefix = (await this.isVersionSupported("r0.6.0")) ? ClientPrefix.R0 : ClientPrefix.Unstable;
return this.http.authedRequest(Method.Post, path, undefined, data, { prefix });
return this.http.authedRequest(Method.Post, path, undefined, data);
}
/**
@@ -8953,9 +8794,7 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
}
public uploadKeySignatures(content: KeySignatures): Promise<IUploadKeySignaturesResponse> {
return this.http.authedRequest(Method.Post, "/keys/signatures/upload", undefined, content, {
prefix: ClientPrefix.V3,
});
return this.http.authedRequest(Method.Post, "/keys/signatures/upload", undefined, content);
}
/**
+2 -2
View File
@@ -49,7 +49,7 @@ export function getHttpUriForMxc(
}
}
let serverAndMediaId = mxc.slice(6); // strips mxc://
let prefix = "/_matrix/media/r0/download/";
let prefix = "/_matrix/media/v3/download/";
const params: Record<string, string> = {};
if (width) {
@@ -64,7 +64,7 @@ export function getHttpUriForMxc(
if (Object.keys(params).length > 0) {
// these are thumbnailing params so they probably want the
// thumbnailing API...
prefix = "/_matrix/media/r0/thumbnail/";
prefix = "/_matrix/media/v3/thumbnail/";
}
const fragmentOffset = serverAndMediaId.indexOf("#");
+22 -1
View File
@@ -20,7 +20,7 @@ import { DeviceMap } from "./models/device";
import { UIAuthCallback } from "./interactive-auth";
import { AddSecretStorageKeyOpts, SecretStorageCallbacks, SecretStorageKeyDescription } from "./secret-storage";
import { VerificationRequest } from "./crypto-api/verification";
import { BackupTrustInfo, KeyBackupInfo } from "./crypto-api/keybackup";
import { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "./crypto-api/keybackup";
import { ISignatures } from "./@types/signed";
/**
@@ -281,6 +281,16 @@ export interface CryptoApi {
*/
findVerificationRequestDMInProgress(roomId: string, userId?: string): VerificationRequest | undefined;
/**
* Request a key verification from another user, using a DM.
*
* @param userId - the user to request verification with.
* @param roomId - the room to use for verification.
*
* @returns resolves to a VerificationRequest when the request has been sent to the other party.
*/
requestVerificationDM(userId: string, roomId: string): Promise<VerificationRequest>;
/**
* Send a verification request to our other devices.
*
@@ -350,6 +360,17 @@ export interface CryptoApi {
* @param info - key backup info dict from {@link MatrixClient#getKeyBackupVersion}.
*/
isKeyBackupTrusted(info: KeyBackupInfo): Promise<BackupTrustInfo>;
/**
* Force a re-check of the key backup and enable/disable it as appropriate.
*
* Fetches the current backup information from the server. If there is a backup, and it is trusted, starts
* backing up to it; otherwise, disables backups.
*
* @returns `null` if there is no backup on the server. Otherwise, data on the backup as returned by the server,
* and trust information (as returned by {@link isKeyBackupTrusted}).
*/
checkKeyBackupAndEnable(): Promise<KeyBackupCheck | null>;
}
/**
+8
View File
@@ -60,3 +60,11 @@ export interface BackupTrustInfo {
*/
readonly matchesDecryptionKey: boolean;
}
/**
* The result of {@link CryptoApi.checkKeyBackupAndEnable}.
*/
export interface KeyBackupCheck {
backupInfo: KeyBackupInfo;
trustInfo: BackupTrustInfo;
}
+19 -11
View File
@@ -440,6 +440,7 @@ export class BackupManager {
* @param maxDelay - Maximum delay to wait in ms. 0 means no delay.
*/
public async scheduleKeyBackupSend(maxDelay = 10000): Promise<void> {
logger.debug(`Key backup: scheduleKeyBackupSend currentSending:${this.sendingBackups} delay:${maxDelay}`);
if (this.sendingBackups) return;
this.sendingBackups = true;
@@ -452,6 +453,7 @@ export class BackupManager {
await sleep(delay);
if (!this.clientRunning) {
logger.debug("Key backup send aborted, client stopped");
this.sendingBackups = false;
return;
}
let numFailures = 0; // number of consecutive failures
@@ -463,24 +465,26 @@ export class BackupManager {
const numBackedUp = await this.backupPendingKeys(KEY_BACKUP_KEYS_PER_REQUEST);
if (numBackedUp === 0) {
// no sessions left needing backup: we're done
this.sendingBackups = false;
return;
}
numFailures = 0;
} catch (err) {
numFailures++;
logger.log("Key backup request failed", err);
if ((<MatrixError>err).data) {
if (
(<MatrixError>err).data.errcode == "M_NOT_FOUND" ||
(<MatrixError>err).data.errcode == "M_WRONG_ROOM_KEYS_VERSION"
) {
// Re-check key backup status on error, so we can be
// sure to present the current situation when asked.
await this.checkKeyBackup();
if (err instanceof MatrixError) {
const errCode = err.data.errcode;
if (errCode == "M_NOT_FOUND" || errCode == "M_WRONG_ROOM_KEYS_VERSION") {
// Set to false now as `checkKeyBackup` might schedule a backupsend before this one ends.
this.sendingBackups = false;
// Backup version has changed or this backup version
// has been deleted
this.baseApis.crypto!.emit(CryptoEvent.KeyBackupFailed, (<MatrixError>err).data.errcode!);
throw err;
this.baseApis.crypto!.emit(CryptoEvent.KeyBackupFailed, errCode);
// Re-check key backup status on error, so we can be
// sure to present the current situation when asked.
// This call might restart the backup loop if new backup version is trusted
await this.checkKeyBackup();
return;
}
}
}
@@ -491,10 +495,14 @@ export class BackupManager {
if (!this.clientRunning) {
logger.debug("Key backup send loop aborted, client stopped");
this.sendingBackups = false;
return;
}
}
} finally {
} catch (err) {
// No one actually checks errors on this promise, it's spawned internally.
// Just log, apps/client should use events to check status
logger.log(`Backup loop failed ${err}`);
this.sendingBackups = false;
}
}
+15
View File
@@ -92,6 +92,7 @@ import {
CrossSigningStatus,
DeviceVerificationStatus,
ImportRoomKeysOpts,
KeyBackupCheck,
KeyBackupInfo,
VerificationRequest as CryptoApiVerificationRequest,
} from "../crypto-api";
@@ -1304,6 +1305,20 @@ export class Crypto extends TypedEventEmitter<CryptoEvent, CryptoEventHandlerMap
return backupTrustInfoFromLegacyTrustInfo(trustInfo);
}
/**
* Force a re-check of the key backup and enable/disable it as appropriate.
*
* Implementation of {@link CryptoApi.checkKeyBackupAndEnable}.
*/
public async checkKeyBackupAndEnable(): Promise<KeyBackupCheck | null> {
const checkResult = await this.backupManager.checkKeyBackup();
if (!checkResult || !checkResult.backupInfo) return null;
return {
backupInfo: checkResult.backupInfo,
trustInfo: backupTrustInfoFromLegacyTrustInfo(checkResult.trustInfo),
};
}
/**
* Checks that a given cross-signing private key matches a given public key.
* This can be used by the getCrossSigningKey callback to verify that the
+3 -3
View File
@@ -110,7 +110,7 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
});
};
const url = this.getUrl("/upload", undefined, MediaPrefix.R0);
const url = this.getUrl("/upload", undefined, MediaPrefix.V3);
if (includeFilename && fileName) {
url.searchParams.set("filename", encodeURIComponent(fileName));
@@ -139,7 +139,7 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
const headers: Record<string, string> = { "Content-Type": contentType };
this.authedRequest<UploadResponse>(Method.Post, "/upload", queryParams, file, {
prefix: MediaPrefix.R0,
prefix: MediaPrefix.V3,
headers,
abortSignal: abortController.signal,
})
@@ -182,7 +182,7 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
public getContentUri(): IContentUri {
return {
base: this.opts.baseUrl,
path: MediaPrefix.R0 + "/upload",
path: MediaPrefix.V3 + "/upload",
params: {
access_token: this.opts.accessToken!,
},
+7 -7
View File
@@ -16,11 +16,7 @@ limitations under the License.
export enum ClientPrefix {
/**
* A constant representing the URI path for release 0 of the Client-Server HTTP API.
*/
R0 = "/_matrix/client/r0",
/**
* A constant representing the URI path for the legacy release v1 of the Client-Server HTTP API.
* A constant representing the URI path for Client-Server API endpoints versioned at v1.
*/
V1 = "/_matrix/client/v1",
/**
@@ -42,7 +38,11 @@ export enum IdentityPrefix {
export enum MediaPrefix {
/**
* URI path for the media repo API
* A constant representing the URI path for Client-Server API Media endpoints versioned at v1.
*/
R0 = "/_matrix/media/r0",
V1 = "/_matrix/media/v3",
/**
* A constant representing the URI path for Client-Server API Media endpoints versioned at v3.
*/
V3 = "/_matrix/media/v3",
}
+2 -4
View File
@@ -345,10 +345,8 @@ export class InteractiveAuth<T> {
sid: this.emailSid,
client_secret: this.clientSecret,
};
if (await this.matrixClient.doesServerRequireIdServerParam()) {
const idServerParsedUrl = new URL(this.matrixClient.getIdentityServerUrl()!);
creds.id_server = idServerParsedUrl.host;
}
const idServerParsedUrl = new URL(this.matrixClient.getIdentityServerUrl()!);
creds.id_server = idServerParsedUrl.host;
authDict = {
type: EMAIL_STAGE_TYPE,
// TODO: Remove `threepid_creds` once servers support proper UIA
+1
View File
@@ -50,6 +50,7 @@ export * from "./service-types";
export * from "./store/memory";
export * from "./store/indexeddb";
export * from "./crypto/store/memory-crypto-store";
export * from "./crypto/store/localStorage-crypto-store";
export * from "./crypto/store/indexeddb-crypto-store";
export type { OutgoingRoomKeyRequest } from "./crypto/store/base";
export * from "./content-repo";
+7 -2
View File
@@ -75,7 +75,12 @@ export class OutgoingRequestProcessor {
} else if (msg instanceof SignatureUploadRequest) {
resp = await this.rawJsonRequest(Method.Post, "/_matrix/client/v3/keys/signatures/upload", {}, msg.body);
} else if (msg instanceof KeysBackupRequest) {
resp = await this.rawJsonRequest(Method.Put, "/_matrix/client/v3/room_keys/keys", {}, msg.body);
resp = await this.rawJsonRequest(
Method.Put,
"/_matrix/client/v3/room_keys/keys",
{ version: msg.version },
msg.body,
);
} else if (msg instanceof ToDeviceRequest) {
const path =
`/_matrix/client/v3/sendToDevice/${encodeURIComponent(msg.event_type)}/` +
@@ -83,7 +88,7 @@ export class OutgoingRequestProcessor {
resp = await this.rawJsonRequest(Method.Put, path, {}, msg.body);
} else if (msg instanceof RoomMessageRequest) {
const path =
`/_matrix/client/v3/room/${encodeURIComponent(msg.room_id)}/send/` +
`/_matrix/client/v3/rooms/${encodeURIComponent(msg.room_id)}/send/` +
`${encodeURIComponent(msg.event_type)}/${encodeURIComponent(msg.txn_id)}`;
resp = await this.rawJsonRequest(Method.Put, path, {}, msg.body);
} else if (msg instanceof SigningKeysUploadRequest) {
+244 -5
View File
@@ -17,20 +17,49 @@ limitations under the License.
import { OlmMachine, SignatureVerification } from "@matrix-org/matrix-sdk-crypto-wasm";
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
import { BackupTrustInfo, Curve25519AuthData, KeyBackupInfo } from "../crypto-api/keybackup";
import { BackupTrustInfo, Curve25519AuthData, KeyBackupCheck, KeyBackupInfo } from "../crypto-api/keybackup";
import { logger } from "../logger";
import { ClientPrefix, IHttpOpts, MatrixError, MatrixHttpApi, Method } from "../http-api";
import { CryptoEvent } from "../crypto";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
import { sleep } from "../utils";
/**
* @internal
*/
export class RustBackupManager {
public constructor(private readonly olmMachine: OlmMachine) {}
export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents, RustBackupCryptoEventMap> {
/** Have we checked if there is a backup on the server which we can use */
private checkedForBackup = false;
private activeBackupVersion: string | null = null;
private stopped = false;
/** whether {@link backupKeysLoop} is currently running */
private backupKeysLoopRunning = false;
public constructor(
private readonly olmMachine: OlmMachine,
private readonly http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,
private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
) {
super();
}
/**
* Tells the RustBackupManager to stop.
* The RustBackupManager is scheduling background uploads of keys to the backup, this
* call allows to cancel the process when the client is stoppped.
*/
public stop(): void {
this.stopped = true;
}
/**
* Get the backup version we are currently backing up to, if any
*/
public async getActiveBackupVersion(): Promise<string | null> {
// TODO stub
return null;
if (!this.olmMachine.isBackupEnabled()) return null;
return this.activeBackupVersion;
}
/**
@@ -52,4 +81,214 @@ export class RustBackupManager {
trusted: signatureVerification.trusted(),
};
}
/**
* Re-check the key backup and enable/disable it as appropriate.
*
* @param force - whether we should force a re-check even if one has already happened.
*/
public checkKeyBackupAndEnable(force: boolean): Promise<KeyBackupCheck | null> {
if (!force && this.checkedForBackup) {
return Promise.resolve(null);
}
// make sure there is only one check going on at a time
if (!this.keyBackupCheckInProgress) {
this.keyBackupCheckInProgress = this.doCheckKeyBackup().finally(() => {
this.keyBackupCheckInProgress = null;
});
}
return this.keyBackupCheckInProgress;
}
private keyBackupCheckInProgress: Promise<KeyBackupCheck | null> | null = null;
/** Helper for `checkKeyBackup` */
private async doCheckKeyBackup(): Promise<KeyBackupCheck | null> {
logger.log("Checking key backup status...");
let backupInfo: KeyBackupInfo | null = null;
try {
backupInfo = await this.requestKeyBackupVersion();
} catch (e) {
logger.warn("Error checking for active key backup", e);
return null;
}
this.checkedForBackup = true;
if (backupInfo && !backupInfo.version) {
logger.warn("active backup lacks a useful 'version'; ignoring it");
}
const activeVersion = await this.getActiveBackupVersion();
if (!backupInfo) {
if (activeVersion !== null) {
logger.log("No key backup present on server: disabling key backup");
await this.disableKeyBackup();
} else {
logger.log("No key backup present on server: not enabling key backup");
}
return null;
}
const trustInfo = await this.isKeyBackupTrusted(backupInfo);
if (!trustInfo.trusted) {
if (activeVersion !== null) {
logger.log("Key backup present on server but not trusted: disabling key backup");
await this.disableKeyBackup();
} else {
logger.log("Key backup present on server but not trusted: not enabling key backup");
}
} else {
if (activeVersion === null) {
logger.log(`Found usable key backup v${backupInfo.version}: enabling key backups`);
await this.enableKeyBackup(backupInfo);
} else if (activeVersion !== backupInfo.version) {
logger.log(`On backup version ${activeVersion} but found version ${backupInfo.version}: switching.`);
// This will remove any pending backup request, remove the backup key and reset the backup state of each room key we have.
await this.disableKeyBackup();
// Enabling will now trigger re-upload of all the keys
await this.enableKeyBackup(backupInfo);
} else {
logger.log(`Backup version ${backupInfo.version} still current`);
}
}
return { backupInfo, trustInfo };
}
private async enableKeyBackup(backupInfo: KeyBackupInfo): Promise<void> {
// we know for certain it must be a Curve25519 key, because we have verified it and only Curve25519
// keys can be verified.
//
// we also checked it has a valid `version`.
await this.olmMachine.enableBackupV1(
(backupInfo.auth_data as Curve25519AuthData).public_key,
backupInfo.version!,
);
this.activeBackupVersion = backupInfo.version!;
this.emit(CryptoEvent.KeyBackupStatus, true);
this.backupKeysLoop();
}
/**
* Restart the backup key loop if there is an active trusted backup.
* Doesn't try to check the backup server side. To be called when a new
* megolm key is known locally.
*/
public async maybeUploadKey(): Promise<void> {
if (this.activeBackupVersion != null) {
this.backupKeysLoop();
}
}
private async disableKeyBackup(): Promise<void> {
await this.olmMachine.disableBackup();
this.activeBackupVersion = null;
this.emit(CryptoEvent.KeyBackupStatus, false);
}
private async backupKeysLoop(maxDelay = 10000): Promise<void> {
if (this.backupKeysLoopRunning) {
logger.log(`Backup loop already running`);
return;
}
this.backupKeysLoopRunning = true;
logger.log(`Starting loop for ${this.activeBackupVersion}.`);
// wait between 0 and `maxDelay` seconds, to avoid backup
// requests from different clients hitting the server all at
// the same time when a new key is sent
const delay = Math.random() * maxDelay;
await sleep(delay);
try {
let numFailures = 0; // number of consecutive network failures for exponential backoff
while (!this.stopped) {
// Get a batch of room keys to upload
const request: RustSdkCryptoJs.KeysBackupRequest | null = await this.olmMachine.backupRoomKeys();
if (!request || this.stopped || !this.activeBackupVersion) {
logger.log(`Ending loop for ${this.activeBackupVersion}.`);
return;
}
try {
await this.outgoingRequestProcessor.makeOutgoingRequest(request);
numFailures = 0;
const keyCount: RustSdkCryptoJs.RoomKeyCounts = await this.olmMachine.roomKeyCounts();
const remaining = keyCount.total - keyCount.backedUp;
this.emit(CryptoEvent.KeyBackupSessionsRemaining, remaining);
} catch (err) {
numFailures++;
logger.error("Error processing backup request for rust crypto-sdk", err);
if (err instanceof MatrixError) {
const errCode = err.data.errcode;
if (errCode == "M_NOT_FOUND" || errCode == "M_WRONG_ROOM_KEYS_VERSION") {
await this.disableKeyBackup();
this.emit(CryptoEvent.KeyBackupFailed, err.data.errcode!);
// There was an active backup and we are out of sync with the server
// force a check server side
this.backupKeysLoopRunning = false;
this.checkKeyBackupAndEnable(true);
return;
} else if (errCode == "M_LIMIT_EXCEEDED") {
// wait for that and then continue?
const waitTime = err.data.retry_after_ms;
if (waitTime > 0) {
sleep(waitTime);
continue;
} // else go to the normal backoff
}
}
// Some other errors (mx, network, or CORS or invalid urls?) anyhow backoff
// exponential backoff if we have failures
await sleep(1000 * Math.pow(2, Math.min(numFailures - 1, 4)));
}
}
} finally {
this.backupKeysLoopRunning = false;
}
}
/**
* Get information about the current key backup from the server
*
* @returns Information object from API or null if there is no active backup.
*/
private async requestKeyBackupVersion(): Promise<KeyBackupInfo | null> {
try {
return await this.http.authedRequest<KeyBackupInfo>(
Method.Get,
"/room_keys/version",
undefined,
undefined,
{
prefix: ClientPrefix.V3,
},
);
} catch (e) {
if ((<MatrixError>e).errcode === "M_NOT_FOUND") {
return null;
} else {
throw e;
}
}
}
}
export type RustBackupCryptoEvents =
| CryptoEvent.KeyBackupStatus
| CryptoEvent.KeyBackupSessionsRemaining
| CryptoEvent.KeyBackupFailed;
export type RustBackupCryptoEventMap = {
[CryptoEvent.KeyBackupStatus]: (enabled: boolean) => void;
[CryptoEvent.KeyBackupSessionsRemaining]: (remaining: number) => void;
[CryptoEvent.KeyBackupFailed]: (errCode: string) => void;
};
+94 -15
View File
@@ -42,6 +42,7 @@ import {
GeneratedSecretStorageKey,
ImportRoomKeyProgressData,
ImportRoomKeysOpts,
KeyBackupCheck,
KeyBackupInfo,
VerificationRequest,
} from "../crypto-api";
@@ -54,11 +55,13 @@ import { secretStorageContainsCrossSigningKeys } from "./secret-storage";
import { keyFromPassphrase } from "../crypto/key_passphrase";
import { encodeRecoveryKey } from "../crypto/recoverykey";
import { crypto } from "../crypto/crypto";
import { RustVerificationRequest, verificationMethodIdentifierToMethod } from "./verification";
import { EventType, MsgType } from "../@types/event";
import { isVerificationEvent, RustVerificationRequest, verificationMethodIdentifierToMethod } from "./verification";
import { EventType } from "../@types/event";
import { CryptoEvent } from "../crypto";
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { RustBackupManager } from "./backup";
import { RustBackupCryptoEventMap, RustBackupCryptoEvents, RustBackupManager } from "./backup";
import { TypedReEmitter } from "../ReEmitter";
import { randomString } from "../randomstring";
const ALL_VERIFICATION_METHODS = ["m.sas.v1", "m.qr_code.scan.v1", "m.qr_code.show.v1", "m.reciprocate.v1"];
@@ -84,8 +87,9 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
private keyClaimManager: KeyClaimManager;
private outgoingRequestProcessor: OutgoingRequestProcessor;
private crossSigningIdentity: CrossSigningIdentity;
private readonly backupManager: RustBackupManager;
public readonly backupManager: RustBackupManager;
private readonly reemitter = new TypedReEmitter<RustCryptoEvents, RustCryptoEventMap>(this);
public constructor(
/** The `OlmMachine` from the underlying rust crypto sdk. */
@@ -114,7 +118,13 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
this.outgoingRequestProcessor = new OutgoingRequestProcessor(olmMachine, http);
this.keyClaimManager = new KeyClaimManager(olmMachine, this.outgoingRequestProcessor);
this.eventDecryptor = new EventDecryptor(olmMachine);
this.backupManager = new RustBackupManager(olmMachine);
this.backupManager = new RustBackupManager(olmMachine, http, this.outgoingRequestProcessor);
this.reemitter.reEmit(this.backupManager, [
CryptoEvent.KeyBackupStatus,
CryptoEvent.KeyBackupSessionsRemaining,
CryptoEvent.KeyBackupFailed,
]);
// Fire if the cross signing keys are imported from the secret storage
const onCrossSigningKeysImport = (): void => {
@@ -143,6 +153,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
this.stopped = true;
this.keyClaimManager.stop();
this.backupManager.stop();
// make sure we close() the OlmMachine; doing so means that all the Rust objects will be
// cleaned up; in particular, the indexeddb connections will be closed, which means they
@@ -704,6 +715,62 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
}
}
/**
* Implementation of {@link CryptoApi#requestVerificationDM}
*/
public async requestVerificationDM(userId: string, roomId: string): Promise<VerificationRequest> {
const userIdentity: RustSdkCryptoJs.UserIdentity | undefined = await this.olmMachine.getIdentity(
new RustSdkCryptoJs.UserId(userId),
);
if (!userIdentity) throw new Error(`unknown userId ${userId}`);
// Transform the verification methods into rust objects
const methods = this._supportedVerificationMethods.map((method) =>
verificationMethodIdentifierToMethod(method),
);
// Get the request content to send to the DM room
const verificationEventContent: string = await userIdentity.verificationRequestContent(methods);
// Send the request content to send to the DM room
const eventId = await this.sendVerificationRequestContent(roomId, verificationEventContent);
// Get a verification request
const request: RustSdkCryptoJs.VerificationRequest = await userIdentity.requestVerification(
new RustSdkCryptoJs.RoomId(roomId),
new RustSdkCryptoJs.EventId(eventId),
methods,
);
return new RustVerificationRequest(request, this.outgoingRequestProcessor, this._supportedVerificationMethods);
}
/**
* Send the verification content to a room
* See https://spec.matrix.org/v1.7/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
*
* Prefer to use {@link OutgoingRequestProcessor.makeOutgoingRequest} when dealing with {@link RustSdkCryptoJs.RoomMessageRequest}
*
* @param roomId - the targeted room
* @param verificationEventContent - the request body.
*
* @returns the event id
*/
private async sendVerificationRequestContent(roomId: string, verificationEventContent: string): Promise<string> {
const txId = randomString(32);
// Send the verification request content to the DM room
const { event_id: eventId } = await this.http.authedRequest<{ event_id: string }>(
Method.Put,
`/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/send/m.room.message/${encodeURIComponent(txId)}`,
undefined,
verificationEventContent,
{
prefix: "",
},
);
return eventId;
}
/**
* The verification methods we offer to the other side during an interactive verification.
*/
@@ -819,6 +886,15 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
return await this.backupManager.isKeyBackupTrusted(info);
}
/**
* Force a re-check of the key backup and enable/disable it as appropriate.
*
* Implementation of {@link Crypto.CryptoApi.checkKeyBackupAndEnable}.
*/
public async checkKeyBackupAndEnable(): Promise<KeyBackupCheck | null> {
return await this.backupManager.checkKeyBackupAndEnable(true);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SyncCryptoCallbacks implementation
@@ -1008,9 +1084,11 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
for (const key of keys) {
this.onRoomKeyUpdated(key);
}
this.backupManager.maybeUploadKey();
}
private onRoomKeyUpdated(key: RustSdkCryptoJs.RoomKeyInfo): void {
if (this.stopped) return;
logger.debug(`Got update for session ${key.senderKey.toBase64()}|${key.sessionId} in ${key.roomId.toString()}`);
const pendingList = this.eventDecryptor.getEventsPendingRoomKey(key);
if (pendingList.length === 0) return;
@@ -1040,15 +1118,13 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
* @param event - live event
*/
public async onLiveEventFromSync(event: MatrixEvent): Promise<void> {
// Ignore state event
if (event.isState()) return;
// Ignore state event or remote echo
// transaction_id is provided in case of remote echo {@link https://spec.matrix.org/v1.7/client-server-api/#local-echo}
if (event.isState() || !!event.getUnsigned().transaction_id) return;
const processEvent = async (evt: MatrixEvent): Promise<void> => {
// Process only key validation request
if (
evt.getType() === EventType.RoomMessage &&
evt.getContent().msgtype === MsgType.KeyVerificationRequest
) {
// Process only verification event
if (isVerificationEvent(event)) {
await this.onKeyVerificationRequest(evt);
}
};
@@ -1058,6 +1134,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
// 5 mins
const TIMEOUT_DELAY = 5 * 60 * 1000;
// After 5mins, we are not expecting the event to be decrypted
const timeoutId = setTimeout(() => event.off(MatrixEventEvent.Decrypted, onDecrypted), TIMEOUT_DELAY);
const onDecrypted = (decryptedEvent: MatrixEvent, error?: Error): void => {
@@ -1067,7 +1144,6 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
event.off(MatrixEventEvent.Decrypted, onDecrypted);
processEvent(decryptedEvent);
};
// After 5mins, we are not expecting the event to be decrypted
event.on(MatrixEventEvent.Decrypted, onDecrypted);
} else {
@@ -1231,7 +1307,10 @@ class EventDecryptor {
}
}
type RustCryptoEvents = CryptoEvent.VerificationRequestReceived | CryptoEvent.UserTrustStatusChanged;
type RustCryptoEvents =
| CryptoEvent.VerificationRequestReceived
| CryptoEvent.UserTrustStatusChanged
| RustBackupCryptoEvents;
type RustCryptoEventMap = {
/**
@@ -1243,4 +1322,4 @@ type RustCryptoEventMap = {
* Fires when the cross signing keys are imported during {@link CryptoApi#bootstrapCrossSigning}
*/
[CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserTrustLevel) => void;
};
} & RustBackupCryptoEventMap;
+27
View File
@@ -31,6 +31,8 @@ import {
import { TypedEventEmitter } from "../models/typed-event-emitter";
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
import { TypedReEmitter } from "../ReEmitter";
import { MatrixEvent } from "../models/event";
import { EventType, MsgType } from "../@types/event";
/**
* An incoming, or outgoing, request to verify a user or a device via cross-signing.
@@ -700,3 +702,28 @@ export function verificationMethodIdentifierToMethod(method: string): RustSdkCry
}
return meth;
}
/**
* Return true if the event's type matches that of an in-room verification event
*
* @param event - MatrixEvent
* @returns
*
* @internal
*/
export function isVerificationEvent(event: MatrixEvent): boolean {
switch (event.getType()) {
case EventType.KeyVerificationCancel:
case EventType.KeyVerificationDone:
case EventType.KeyVerificationMac:
case EventType.KeyVerificationStart:
case EventType.KeyVerificationKey:
case EventType.KeyVerificationReady:
case EventType.KeyVerificationAccept:
return true;
case EventType.RoomMessage:
return event.getContent().msgtype === MsgType.KeyVerificationRequest;
default:
return false;
}
}
+4 -11
View File
@@ -649,18 +649,11 @@ export class SyncApi {
this.opts.lazyLoadMembers = false;
}
if (this.opts.lazyLoadMembers) {
debuglog("Checking server lazy load support...");
const supported = await this.client.doesServerSupportLazyLoading();
if (supported) {
debuglog("Enabling lazy load on sync filter...");
if (!this.opts.filter) {
this.opts.filter = this.buildDefaultFilter();
}
this.opts.filter.setLazyLoadMembers(true);
} else {
debuglog("LL: lazy loading requested but not supported " + "by server, so disabling");
this.opts.lazyLoadMembers = false;
debuglog("Enabling lazy load on sync filter...");
if (!this.opts.filter) {
this.opts.filter = this.buildDefaultFilter();
}
this.opts.filter.setLazyLoadMembers(true);
}
// need to vape the store when enabling LL and wasn't enabled before
debuglog("Checking whether lazy loading has changed in store...");
+20
View File
@@ -0,0 +1,20 @@
/*
Copyright 2022 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* The minimum Matrix specification version the js-sdk supports.
*/
export const MINIMUM_MATRIX_VERSION = "v1.1";
+261 -290
View File
@@ -26,9 +26,9 @@
"@octokit/plugin-rest-endpoint-methods" "^5.13.0"
"@actions/http-client@^2.0.1":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.1.0.tgz#b6d8c3934727d6a50d10d19f00a711a964599a9f"
integrity sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==
version "2.1.1"
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.1.1.tgz#a8e97699c315bed0ecaeaaeb640948470d4586a0"
integrity sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==
dependencies:
tunnel "^0.0.6"
@@ -41,9 +41,9 @@
"@jridgewell/trace-mapping" "^0.3.9"
"@babel/cli@^7.12.10":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.9.tgz#501b3614aeda7399371f6d5991404f069b059986"
integrity sha512-nb2O7AThqRo7/E53EGiuAkMaRbb7J5Qp3RvN+dmua1U+kydm0oznkhqbTEG15yk26G/C3yL6OdZjzgl+DMXVVA==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.10.tgz#25e4bbd8d0a0d8b4b389e1b5e2d7a238bd4c1b75"
integrity sha512-rM9ZMmaII630zGvtMtQ3P4GyHs28CHLYE9apLG7L8TgaSqcfoIGrlLSLsh4Q8kDTdZQQEXZm1M0nQtOvU/2heg==
dependencies:
"@jridgewell/trace-mapping" "^0.3.17"
commander "^4.0.1"
@@ -56,12 +56,13 @@
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
chokidar "^3.4.0"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658"
integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3"
integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==
dependencies:
"@babel/highlight" "^7.22.5"
"@babel/highlight" "^7.22.10"
chalk "^2.4.2"
"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9":
version "7.22.9"
@@ -69,20 +70,20 @@
integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==
"@babel/core@^7.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f"
integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35"
integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.22.5"
"@babel/generator" "^7.22.9"
"@babel/helper-compilation-targets" "^7.22.9"
"@babel/code-frame" "^7.22.10"
"@babel/generator" "^7.22.10"
"@babel/helper-compilation-targets" "^7.22.10"
"@babel/helper-module-transforms" "^7.22.9"
"@babel/helpers" "^7.22.6"
"@babel/parser" "^7.22.7"
"@babel/helpers" "^7.22.10"
"@babel/parser" "^7.22.10"
"@babel/template" "^7.22.5"
"@babel/traverse" "^7.22.8"
"@babel/types" "^7.22.5"
"@babel/traverse" "^7.22.10"
"@babel/types" "^7.22.10"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
@@ -90,27 +91,27 @@
semver "^6.3.1"
"@babel/eslint-parser@^7.12.10":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.9.tgz#75f8aa978d1e76c87cc6f26c1ea16ae58804d390"
integrity sha512-xdMkt39/nviO/4vpVdrEYPwXCsYIXSSAr6mC7WQsNIlGnuxKyKE7GZjalcnbSWiC4OXGNNN3UQPeHfjSC6sTDA==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.10.tgz#bfdf3d1b32ad573fe7c1c3447e0b485e3a41fd09"
integrity sha512-0J8DNPRXQRLeR9rPaUMM3fA+RbixjnVLe/MRMYCkp3hzgsSuxCHQ8NN8xQG1wIHKJ4a1DTROTvFJdW+B5/eOsg==
dependencies:
"@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
eslint-visitor-keys "^2.1.0"
semver "^6.3.1"
"@babel/eslint-plugin@^7.12.10":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.22.5.tgz#47407d8c9e527b62ff75ee11e4baa6de3da7cf0e"
integrity sha512-lDXW06rf1sXywWWw+UdS/iYxRjrqhH4AXdPeKE4+fEgEoGBXcdIDQ+uCJOUcvCb0jCTvfwHOSXkwnfd24EAkLQ==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.22.10.tgz#b84e0f029b8f78604c3f6797cd6208cad46fbcf5"
integrity sha512-SRZcvo3fnO5h79B9DZSV6LG2vHH7OWsSNp1huFLHsXKyytRG413byQk9zxW1VcPOhnzfx2VIUz+8aGbiE7fOkA==
dependencies:
eslint-rule-composer "^0.3.0"
"@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d"
integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==
"@babel/generator@^7.22.10", "@babel/generator@^7.7.2":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722"
integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==
dependencies:
"@babel/types" "^7.22.5"
"@babel/types" "^7.22.10"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
@@ -123,16 +124,16 @@
"@babel/types" "^7.22.5"
"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878"
integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz#573e735937e99ea75ea30788b57eb52fab7468c9"
integrity sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==
dependencies:
"@babel/types" "^7.22.5"
"@babel/types" "^7.22.10"
"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892"
integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==
"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.10", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024"
integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==
dependencies:
"@babel/compat-data" "^7.22.9"
"@babel/helper-validator-option" "^7.22.5"
@@ -140,10 +141,10 @@
lru-cache "^5.1.1"
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.5", "@babel/helper-create-class-features-plugin@^7.22.9":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236"
integrity sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==
"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.10", "@babel/helper-create-class-features-plugin@^7.22.5":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3"
integrity sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA==
dependencies:
"@babel/helper-annotate-as-pure" "^7.22.5"
"@babel/helper-environment-visitor" "^7.22.5"
@@ -232,7 +233,7 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
"@babel/helper-remap-async-to-generator@^7.22.5":
"@babel/helper-remap-async-to-generator@^7.22.5", "@babel/helper-remap-async-to-generator@^7.22.9":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82"
integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==
@@ -287,36 +288,36 @@
integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==
"@babel/helper-wrap-function@^7.22.9":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9"
integrity sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz#d845e043880ed0b8c18bd194a12005cb16d2f614"
integrity sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==
dependencies:
"@babel/helper-function-name" "^7.22.5"
"@babel/template" "^7.22.5"
"@babel/types" "^7.22.5"
"@babel/types" "^7.22.10"
"@babel/helpers@^7.22.6":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd"
integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==
"@babel/helpers@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a"
integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==
dependencies:
"@babel/template" "^7.22.5"
"@babel/traverse" "^7.22.6"
"@babel/types" "^7.22.5"
"@babel/traverse" "^7.22.10"
"@babel/types" "^7.22.10"
"@babel/highlight@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031"
integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==
"@babel/highlight@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7"
integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==
dependencies:
"@babel/helper-validator-identifier" "^7.22.5"
chalk "^2.0.0"
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7":
version "7.22.7"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae"
integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55"
integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5":
version "7.22.5"
@@ -366,14 +367,6 @@
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e"
integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==
dependencies:
"@babel/helper-create-regexp-features-plugin" "^7.18.6"
"@babel/helper-plugin-utils" "^7.18.6"
"@babel/plugin-syntax-async-generators@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -529,14 +522,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-async-generator-functions@^7.22.7":
version "7.22.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz#053e76c0a903b72b573cb1ab7d6882174d460a1b"
integrity sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==
"@babel/plugin-transform-async-generator-functions@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz#45946cd17f915b10e65c29b8ed18a0a50fc648c8"
integrity sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==
dependencies:
"@babel/helper-environment-visitor" "^7.22.5"
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/helper-remap-async-to-generator" "^7.22.5"
"@babel/helper-remap-async-to-generator" "^7.22.9"
"@babel/plugin-syntax-async-generators" "^7.8.4"
"@babel/plugin-transform-async-to-generator@^7.22.5":
@@ -555,10 +548,10 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-block-scoping@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b"
integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==
"@babel/plugin-transform-block-scoping@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz#88a1dccc3383899eb5e660534a76a22ecee64faa"
integrity sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
@@ -602,14 +595,14 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/template" "^7.22.5"
"@babel/plugin-transform-destructuring@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc"
integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==
"@babel/plugin-transform-destructuring@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz#38e2273814a58c810b6c34ea293be4973c4eb5e2"
integrity sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-dotall-regex@^7.22.5", "@babel/plugin-transform-dotall-regex@^7.4.4":
"@babel/plugin-transform-dotall-regex@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165"
integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==
@@ -787,10 +780,10 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
"@babel/plugin-transform-optional-chaining@^7.22.5", "@babel/plugin-transform-optional-chaining@^7.22.6":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564"
integrity sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==
"@babel/plugin-transform-optional-chaining@^7.22.10", "@babel/plugin-transform-optional-chaining@^7.22.5":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz#076d28a7e074392e840d4ae587d83445bac0372a"
integrity sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
@@ -828,13 +821,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-regenerator@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa"
integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==
"@babel/plugin-transform-regenerator@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz#8ceef3bd7375c4db7652878b0241b2be5d0c3cca"
integrity sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
regenerator-transform "^0.15.1"
regenerator-transform "^0.15.2"
"@babel/plugin-transform-reserved-words@^7.22.5":
version "7.22.5"
@@ -844,15 +837,15 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-runtime@^7.12.10":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz#a87b11e170cbbfb018e6a2bf91f5c6e533b9e027"
integrity sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.10.tgz#89eda6daf1d3af6f36fb368766553054c8d7cd46"
integrity sha512-RchI7HePu1eu0CYNKHHHQdfenZcM4nz8rew5B1VWqeRKdcwW5aQ5HeG9eTUbWiAS1UrmHVLmoxTWHt3iLD/NhA==
dependencies:
"@babel/helper-module-imports" "^7.22.5"
"@babel/helper-plugin-utils" "^7.22.5"
babel-plugin-polyfill-corejs2 "^0.4.4"
babel-plugin-polyfill-corejs3 "^0.8.2"
babel-plugin-polyfill-regenerator "^0.5.1"
babel-plugin-polyfill-corejs2 "^0.4.5"
babel-plugin-polyfill-corejs3 "^0.8.3"
babel-plugin-polyfill-regenerator "^0.5.2"
semver "^6.3.1"
"@babel/plugin-transform-shorthand-properties@^7.22.5":
@@ -892,19 +885,19 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-transform-typescript@^7.22.5":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz#91e08ad1eb1028ecc62662a842e93ecfbf3c7234"
integrity sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.10.tgz#aadd98fab871f0bb5717bcc24c31aaaa455af923"
integrity sha512-7++c8I/ymsDo4QQBAgbraXLzIM6jmfao11KgIBEYZRReWzNWH9NtNgJcyrZiXsOPh523FQm6LfpLyy/U5fn46A==
dependencies:
"@babel/helper-annotate-as-pure" "^7.22.5"
"@babel/helper-create-class-features-plugin" "^7.22.9"
"@babel/helper-create-class-features-plugin" "^7.22.10"
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-syntax-typescript" "^7.22.5"
"@babel/plugin-transform-unicode-escapes@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c"
integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==
"@babel/plugin-transform-unicode-escapes@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz#c723f380f40a2b2f57a62df24c9005834c8616d9"
integrity sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
@@ -933,12 +926,12 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/preset-env@^7.12.11":
version "7.22.9"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.9.tgz#57f17108eb5dfd4c5c25a44c1977eba1df310ac7"
integrity sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.10.tgz#3263b9fe2c8823d191d28e61eac60a79f9ce8a0f"
integrity sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A==
dependencies:
"@babel/compat-data" "^7.22.9"
"@babel/helper-compilation-targets" "^7.22.9"
"@babel/helper-compilation-targets" "^7.22.10"
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/helper-validator-option" "^7.22.5"
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5"
@@ -963,15 +956,15 @@
"@babel/plugin-syntax-top-level-await" "^7.14.5"
"@babel/plugin-syntax-unicode-sets-regex" "^7.18.6"
"@babel/plugin-transform-arrow-functions" "^7.22.5"
"@babel/plugin-transform-async-generator-functions" "^7.22.7"
"@babel/plugin-transform-async-generator-functions" "^7.22.10"
"@babel/plugin-transform-async-to-generator" "^7.22.5"
"@babel/plugin-transform-block-scoped-functions" "^7.22.5"
"@babel/plugin-transform-block-scoping" "^7.22.5"
"@babel/plugin-transform-block-scoping" "^7.22.10"
"@babel/plugin-transform-class-properties" "^7.22.5"
"@babel/plugin-transform-class-static-block" "^7.22.5"
"@babel/plugin-transform-classes" "^7.22.6"
"@babel/plugin-transform-computed-properties" "^7.22.5"
"@babel/plugin-transform-destructuring" "^7.22.5"
"@babel/plugin-transform-destructuring" "^7.22.10"
"@babel/plugin-transform-dotall-regex" "^7.22.5"
"@babel/plugin-transform-duplicate-keys" "^7.22.5"
"@babel/plugin-transform-dynamic-import" "^7.22.5"
@@ -994,38 +987,36 @@
"@babel/plugin-transform-object-rest-spread" "^7.22.5"
"@babel/plugin-transform-object-super" "^7.22.5"
"@babel/plugin-transform-optional-catch-binding" "^7.22.5"
"@babel/plugin-transform-optional-chaining" "^7.22.6"
"@babel/plugin-transform-optional-chaining" "^7.22.10"
"@babel/plugin-transform-parameters" "^7.22.5"
"@babel/plugin-transform-private-methods" "^7.22.5"
"@babel/plugin-transform-private-property-in-object" "^7.22.5"
"@babel/plugin-transform-property-literals" "^7.22.5"
"@babel/plugin-transform-regenerator" "^7.22.5"
"@babel/plugin-transform-regenerator" "^7.22.10"
"@babel/plugin-transform-reserved-words" "^7.22.5"
"@babel/plugin-transform-shorthand-properties" "^7.22.5"
"@babel/plugin-transform-spread" "^7.22.5"
"@babel/plugin-transform-sticky-regex" "^7.22.5"
"@babel/plugin-transform-template-literals" "^7.22.5"
"@babel/plugin-transform-typeof-symbol" "^7.22.5"
"@babel/plugin-transform-unicode-escapes" "^7.22.5"
"@babel/plugin-transform-unicode-escapes" "^7.22.10"
"@babel/plugin-transform-unicode-property-regex" "^7.22.5"
"@babel/plugin-transform-unicode-regex" "^7.22.5"
"@babel/plugin-transform-unicode-sets-regex" "^7.22.5"
"@babel/preset-modules" "^0.1.5"
"@babel/types" "^7.22.5"
babel-plugin-polyfill-corejs2 "^0.4.4"
babel-plugin-polyfill-corejs3 "^0.8.2"
babel-plugin-polyfill-regenerator "^0.5.1"
"@babel/preset-modules" "0.1.6-no-external-plugins"
"@babel/types" "^7.22.10"
babel-plugin-polyfill-corejs2 "^0.4.5"
babel-plugin-polyfill-corejs3 "^0.8.3"
babel-plugin-polyfill-regenerator "^0.5.2"
core-js-compat "^3.31.0"
semver "^6.3.1"
"@babel/preset-modules@^0.1.5":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6.tgz#31bcdd8f19538437339d17af00d177d854d9d458"
integrity sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==
"@babel/preset-modules@0.1.6-no-external-plugins":
version "0.1.6-no-external-plugins"
resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a"
integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
"@babel/plugin-transform-dotall-regex" "^7.4.4"
"@babel/types" "^7.4.4"
esutils "^2.0.2"
@@ -1057,11 +1048,11 @@
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682"
integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==
dependencies:
regenerator-runtime "^0.13.11"
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.5", "@babel/template@^7.3.3":
version "7.22.5"
@@ -1072,26 +1063,26 @@
"@babel/parser" "^7.22.5"
"@babel/types" "^7.22.5"
"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8":
version "7.22.8"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e"
integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==
"@babel/traverse@^7.22.10":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa"
integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==
dependencies:
"@babel/code-frame" "^7.22.5"
"@babel/generator" "^7.22.7"
"@babel/code-frame" "^7.22.10"
"@babel/generator" "^7.22.10"
"@babel/helper-environment-visitor" "^7.22.5"
"@babel/helper-function-name" "^7.22.5"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.22.7"
"@babel/types" "^7.22.5"
"@babel/parser" "^7.22.10"
"@babel/types" "^7.22.10"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe"
integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==
"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
version "7.22.10"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03"
integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.5"
@@ -1118,12 +1109,12 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@es-joy/jsdoccomment@~0.39.4":
version "0.39.4"
resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz#6b8a62e9b3077027837728818d3c4389a898b392"
integrity sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==
"@es-joy/jsdoccomment@~0.40.1":
version "0.40.1"
resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz#13acd77fb372ed1c83b7355edd865a3b370c9ec4"
integrity sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==
dependencies:
comment-parser "1.3.1"
comment-parser "1.4.0"
esquery "^1.5.0"
jsdoc-type-pratt-parser "~4.0.0"
@@ -1134,15 +1125,15 @@
dependencies:
eslint-visitor-keys "^3.3.0"
"@eslint-community/regexpp@^4.4.0":
"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
version "4.6.2"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8"
integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==
"@eslint/eslintrc@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d"
integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==
"@eslint/eslintrc@^2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.1.tgz#18d635e24ad35f7276e8a49d135c7d3ca6a46f93"
integrity sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
@@ -1154,10 +1145,10 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@8.44.0":
version "8.44.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af"
integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==
"@eslint/js@^8.46.0":
version "8.46.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6"
integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==
"@humanwhocodes/config-array@^0.11.10":
version "0.11.10"
@@ -1433,12 +1424,7 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
"@jridgewell/resolve-uri@^3.0.3":
"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
@@ -1456,12 +1442,7 @@
"@jridgewell/gen-mapping" "^0.3.0"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/sourcemap-codec@1.4.14":
version "1.4.14"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/sourcemap-codec@^1.4.10":
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@@ -1475,21 +1456,20 @@
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.18"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6"
integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==
version "0.3.19"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811"
integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==
dependencies:
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@matrix-org/matrix-sdk-crypto-wasm@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.2.0.tgz#115cd21cb2bba3c8166cf09e7d61da0902aa8973"
integrity sha512-vmpbtXYFzfBSFjeAx/PNRjy7zyH+Xx2HVXNKdApgo3+hSALewcXwdOTJy5pKq+poumM2TjjKDhG2s6/zSDNUYg==
"@matrix-org/matrix-sdk-crypto-wasm@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.2.1.tgz#5b546c8a0e53b614f10b77b3b649818aed9d0db1"
integrity sha512-DCb7Q83PCQK0uav5vB3KNV/hJPlxAhT/ddar+VHz2kC39hMLKGzWYVhprpLYVcavaE/6OX+Q/xFkAoV/3QtUHQ==
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
version "3.2.14"
uid acd96c00a881d0f462e1f97a56c73742c8dbc984
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984"
"@microsoft/tsdoc-config@0.16.2":
@@ -1830,14 +1810,14 @@
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/node@*":
version "20.4.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.5.tgz#9dc0a5cb1ccce4f7a731660935ab70b9c00a5d69"
integrity sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==
version "20.4.8"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85"
integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==
"@types/node@18":
version "18.17.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.1.tgz#84c32903bf3a09f7878c391d31ff08f6fe7d8335"
integrity sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==
version "18.17.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.3.tgz#409febdc84478b452306a8112c692e800ad9f6fe"
integrity sha512-2x8HWtFk0S99zqVQABU9wTpr8wPoaDHZUcAkoTKH+nL7kPv3WUI9cRi/Kk5Mz4xdqXSqTkKP7IWNoQQYCnDsTA==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
@@ -2037,7 +2017,7 @@ agent-base@6:
dependencies:
debug "4"
ajv@^6.10.0, ajv@^6.12.4, ajv@~6.12.6:
ajv@^6.12.4, ajv@~6.12.6:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2286,7 +2266,7 @@ babel-plugin-jest-hoist@^29.5.0:
"@types/babel__core" "^7.1.14"
"@types/babel__traverse" "^7.0.6"
babel-plugin-polyfill-corejs2@^0.4.4:
babel-plugin-polyfill-corejs2@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c"
integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==
@@ -2295,7 +2275,7 @@ babel-plugin-polyfill-corejs2@^0.4.4:
"@babel/helper-define-polyfill-provider" "^0.4.2"
semver "^6.3.1"
babel-plugin-polyfill-corejs3@^0.8.2:
babel-plugin-polyfill-corejs3@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz#b4f719d0ad9bb8e0c23e3e630c0c8ec6dd7a1c52"
integrity sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==
@@ -2303,7 +2283,7 @@ babel-plugin-polyfill-corejs3@^0.8.2:
"@babel/helper-define-polyfill-provider" "^0.4.2"
core-js-compat "^3.31.0"
babel-plugin-polyfill-regenerator@^0.5.1:
babel-plugin-polyfill-regenerator@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326"
integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==
@@ -2572,13 +2552,13 @@ browserify@^17.0.0:
xtend "^4.0.0"
browserslist@^4.21.9:
version "4.21.9"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635"
integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==
version "4.21.10"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0"
integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==
dependencies:
caniuse-lite "^1.0.30001503"
electron-to-chromium "^1.4.431"
node-releases "^2.0.12"
caniuse-lite "^1.0.30001517"
electron-to-chromium "^1.4.477"
node-releases "^2.0.13"
update-browserslist-db "^1.0.11"
bs58@^5.0.0:
@@ -2658,12 +2638,12 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001503:
version "1.0.30001517"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8"
integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==
caniuse-lite@^1.0.30001517:
version "1.0.30001519"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601"
integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==
chalk@^2.0.0, chalk@^2.4.1:
chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -2823,10 +2803,10 @@ commander@^4.0.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
comment-parser@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b"
integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==
comment-parser@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.0.tgz#0f8c560f59698193854f12884c20c0e39a26d32c"
integrity sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==
commondir@^1.0.1:
version "1.0.1"
@@ -2879,16 +2859,16 @@ convert-source-map@~1.1.0:
integrity sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==
core-js-compat@^3.31.0:
version "3.31.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0"
integrity sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==
version "3.32.0"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.0.tgz#f41574b6893ab15ddb0ac1693681bd56c8550a90"
integrity sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw==
dependencies:
browserslist "^4.21.9"
core-js@^3.0.0:
version "3.31.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.1.tgz#f2b0eea9be9da0def2c5fece71064a7e5d687653"
integrity sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==
version "3.32.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.0.tgz#7643d353d899747ab1f8b03d2803b0312a0fb3b6"
integrity sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==
core-util-is@~1.0.0:
version "1.0.3"
@@ -3021,9 +3001,9 @@ decimal.js@^10.4.2:
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
dedent@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.2.0.tgz#32039cd75c035f684e01c4a07cb88c0ecbeb57be"
integrity sha512-i4tcg0ClgvMUSxwHpt+NHQ01ZJmAkl6eBvDNrSZG9e+oLRTCSHv0wpr/Bzjpf6CwKeIHGevE1M34Y1Axdms5VQ==
version "1.5.1"
resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff"
integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==
deep-is@^0.1.3:
version "0.1.4"
@@ -3194,10 +3174,10 @@ eastasianwidth@^0.2.0:
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
electron-to-chromium@^1.4.431:
version "1.4.473"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.473.tgz#4853de13a335c70fe1f9df8d4029be54068767d1"
integrity sha512-aVfC8+440vGfl06l8HKKn8/PD5jRfSnLkTTD65EFvU46igbpQRri1gxSzW9/+TeUlwYzrXk1sw867T96zlyECA==
electron-to-chromium@^1.4.477:
version "1.4.487"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.487.tgz#e2ef8b15f2791bf68fa6f38f2656f1a551d360ae"
integrity sha512-XbCRs/34l31np/p33m+5tdBrdXu9jJkZxSbNxj5I0H1KtV2ZMSB+i/HYqDiRzHaFx2T5EdytjoBRe8QRJE2vQg==
elliptic@^6.5.3:
version "6.5.4"
@@ -3389,19 +3369,19 @@ eslint-config-google@^0.14.0:
resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a"
integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==
eslint-config-prettier@^8.5.0:
version "8.9.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.9.0.tgz#094b6254b2804b0544f7cee535f802b6d29ee10b"
integrity sha512-+sbni7NfVXnOpnRadUA8S28AUlsZt9GjgFvABIRL9Hkn8KqNzOp+7Lw4QWtrwn20KzU3wqu1QoOj2m+7rKRqkA==
eslint-config-prettier@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f"
integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==
eslint-import-resolver-node@^0.3.7:
version "0.3.7"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7"
integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==
version "0.3.9"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac"
integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==
dependencies:
debug "^3.2.7"
is-core-module "^2.11.0"
resolve "^1.22.1"
is-core-module "^2.13.0"
resolve "^1.22.4"
eslint-import-resolver-typescript@^3.5.1:
version "3.5.5"
@@ -3456,13 +3436,13 @@ eslint-plugin-jest@^27.1.6:
"@typescript-eslint/utils" "^5.10.0"
eslint-plugin-jsdoc@^46.0.0:
version "46.4.5"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.5.tgz#f06fd71505d60078a8e4dff792f7cb20261e7e58"
integrity sha512-HjTuxqDYplAQFu29F3MHFCDDBgeqOxPXI6TyBhL0u2rr4XntJ0z3C9PmJvpjFscKdHwkZDN/0l1QCG0QwyRi4g==
version "46.4.6"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz#5226461eda61b5920297cbe02c3b17bc9423cf0b"
integrity sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==
dependencies:
"@es-joy/jsdoccomment" "~0.39.4"
"@es-joy/jsdoccomment" "~0.40.1"
are-docs-informative "^0.0.2"
comment-parser "1.3.1"
comment-parser "1.4.0"
debug "^4.3.4"
escape-string-regexp "^4.0.0"
esquery "^1.5.0"
@@ -3471,9 +3451,9 @@ eslint-plugin-jsdoc@^46.0.0:
spdx-expression-parse "^3.0.1"
eslint-plugin-matrix-org@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-1.2.0.tgz#84b78969c93e6d3d593fe8bf25ee67ec4dcd2883"
integrity sha512-Wp5CeLnyEwGBn8ZfVbSuO2y0Fs51IWonPJ1QRQTntaRxOkEQnnky3gOPwpfGJ8JB0CxYr1zXfeHh8LcYHW4wcg==
version "1.2.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-1.2.1.tgz#76d1505daa93fb99ba4156008b9b32f57682c9b1"
integrity sha512-A3cDjhG7RHwfCS8o3bOip8hSCsxtmgk2ahvqE5v/Ic2kPEZxixY6w8zLj7hFGsrRmPSEpLWqkVLt8uvQBapiQA==
eslint-plugin-tsdoc@^0.2.17:
version "0.2.17"
@@ -3517,10 +3497,10 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1:
esrecurse "^4.3.0"
estraverse "^4.1.1"
eslint-scope@^7.2.0:
version "7.2.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.1.tgz#936821d3462675f25a18ac5fd88a67cc15b393bd"
integrity sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==
eslint-scope@^7.2.2:
version "7.2.2"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
dependencies:
esrecurse "^4.3.0"
estraverse "^5.2.0"
@@ -3530,32 +3510,32 @@ eslint-visitor-keys@^2.1.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz#8c2095440eca8c933bedcadf16fefa44dbe9ba5f"
integrity sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==
eslint@8.45.0:
version "8.45.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.45.0.tgz#bab660f90d18e1364352c0a6b7c6db8edb458b78"
integrity sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==
eslint@8.46.0:
version "8.46.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.46.0.tgz#a06a0ff6974e53e643acc42d1dcf2e7f797b3552"
integrity sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==
dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.4.0"
"@eslint/eslintrc" "^2.1.0"
"@eslint/js" "8.44.0"
"@eslint-community/regexpp" "^4.6.1"
"@eslint/eslintrc" "^2.1.1"
"@eslint/js" "^8.46.0"
"@humanwhocodes/config-array" "^0.11.10"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
ajv "^6.10.0"
ajv "^6.12.4"
chalk "^4.0.0"
cross-spawn "^7.0.2"
debug "^4.3.2"
doctrine "^3.0.0"
escape-string-regexp "^4.0.0"
eslint-scope "^7.2.0"
eslint-visitor-keys "^3.4.1"
espree "^9.6.0"
eslint-scope "^7.2.2"
eslint-visitor-keys "^3.4.2"
espree "^9.6.1"
esquery "^1.4.2"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
@@ -3578,7 +3558,7 @@ eslint@8.45.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
espree@^9.6.0:
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
@@ -4363,10 +4343,10 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
is-core-module@^2.1.0, is-core-module@^2.11.0, is-core-module@^2.12.0, is-core-module@^2.12.1:
version "2.12.1"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd"
integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==
is-core-module@^2.1.0, is-core-module@^2.11.0, is-core-module@^2.12.1, is-core-module@^2.13.0:
version "2.13.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db"
integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==
dependencies:
has "^1.0.3"
@@ -5550,7 +5530,7 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
node-releases@^2.0.12:
node-releases@^2.0.13:
version "2.0.13"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
@@ -6125,15 +6105,15 @@ regenerate@^1.4.2:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
regenerator-transform@^0.15.1:
version "0.15.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56"
integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==
regenerator-transform@^0.15.2:
version "0.15.2"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4"
integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==
dependencies:
"@babel/runtime" "^7.8.4"
@@ -6214,21 +6194,12 @@ resolve.exports@^2.0.0:
resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
resolve@^1.1.4, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.4.0:
version "1.22.2"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f"
integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==
resolve@^1.1.4, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.3, resolve@^1.22.4, resolve@^1.4.0:
version "1.22.4"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34"
integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==
dependencies:
is-core-module "^2.11.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
resolve@^1.22.3:
version "1.22.3"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283"
integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==
dependencies:
is-core-module "^2.12.0"
is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
@@ -6428,9 +6399,9 @@ signal-exit@^3.0.3, signal-exit@^3.0.7:
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
signal-exit@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967"
integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==
version "4.1.0"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04"
integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
simple-concat@^1.0.0:
version "1.0.1"
@@ -7041,9 +7012,9 @@ typedoc-plugin-mdn-links@^3.0.3:
integrity sha512-NXhIpwQnsg7BcyMCHVqj3tUK+DL4g3Bt96JbFl4APzTGFkA+iM6GfZ/fn3TAqJ8O0CXG5R9BfWxolw1m1omNuQ==
typedoc-plugin-missing-exports@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.0.0.tgz#9bdc4e30b0c7f24e9f1cb8890db4d01f608717c5"
integrity sha512-t0QlKCm27/8DaheJkLo/gInSNjzBXgSciGhoLpL6sLyXZibm7SuwJtHvg4qXI2IjJfFBgW9mJvvszpoxMyB0TA==
version "2.0.1"
resolved "https://registry.yarnpkg.com/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.0.1.tgz#76b7d499b2b6fe6fd119b02a0726391c0fe17da8"
integrity sha512-+A78kT78uC0Dbv2EFB9RXHO3iywJ5x89jd4z0bLL7Z8DlOSQjJxhRHf8otXsHZbgRUWAuYqYMA9LzxfuSOc87A==
typedoc-plugin-versions-cli@^0.1.12:
version "0.1.12"