index : matrix-js-sdk

My fork of matrix-js-sdk

diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md251
-rw-r--r--package.json4
-rwxr-xr-xrelease.sh41
-rw-r--r--spec/browserify/setupTests.js23
-rw-r--r--spec/browserify/sync-browserify.spec.js103
-rw-r--r--spec/integ/matrix-client-opts.spec.js5
-rw-r--r--spec/olm-loader.js9
-rw-r--r--spec/unit/crypto.spec.js4
-rw-r--r--spec/unit/crypto/CrossSigningInfo.spec.js6
-rw-r--r--spec/unit/crypto/backup.spec.js2
-rw-r--r--spec/unit/crypto/secrets.spec.js336
-rw-r--r--spec/unit/crypto/verification/sas.spec.js15
-rw-r--r--spec/unit/crypto/verification/verification_request.spec.js36
-rw-r--r--spec/unit/filter-component.spec.js34
-rw-r--r--spec/unit/interactive-auth.spec.js31
-rw-r--r--src/@types/global.d.ts25
-rw-r--r--src/base-apis.js3
-rw-r--r--src/client.js293
-rw-r--r--src/crypto/CrossSigning.js26
-rw-r--r--src/crypto/DeviceList.js8
-rw-r--r--src/crypto/OlmDevice.js16
-rw-r--r--src/crypto/OutgoingRoomKeyRequestManager.js28
-rw-r--r--src/crypto/SecretStorage.js214
-rw-r--r--src/crypto/aes.js4
-rw-r--r--src/crypto/algorithms/megolm.js43
-rw-r--r--src/crypto/algorithms/olm.js19
-rw-r--r--src/crypto/index.js436
-rw-r--r--src/crypto/olmlib.js32
-rw-r--r--src/crypto/store/localStorage-crypto-store.js4
-rw-r--r--src/crypto/verification/Base.js8
-rw-r--r--src/crypto/verification/QRCode.js71
-rw-r--r--src/crypto/verification/SAS.js73
-rw-r--r--src/crypto/verification/request/VerificationRequest.js77
-rw-r--r--src/filter-component.js5
-rw-r--r--src/filter.js12
-rw-r--r--src/http-api.js94
-rw-r--r--src/interactive-auth.js41
-rw-r--r--src/matrix.ts (renamed from src/matrix.js)24
-rw-r--r--src/models/event.js16
-rw-r--r--src/models/room.js4
-rw-r--r--src/pushprocessor.js66
-rw-r--r--src/store/memory.js27
-rw-r--r--src/sync.js25
-rw-r--r--src/utils.ts4
-rw-r--r--tsconfig.json1
-rw-r--r--yarn.lock829
46 files changed, 2288 insertions, 1140 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 684bc03d..af2765c9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,254 @@
+Changes in [6.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.2.0) (2020-06-04)
+================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.2.0-rc.1...v6.2.0)
+
+ * No changes since rc.1
+
+Changes in [6.2.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.2.0-rc.1) (2020-06-02)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.1.0...v6.2.0-rc.1)
+
+ * Make auth argument in the register request compliant with r0.6.0
+ [\#1304](https://github.com/matrix-org/matrix-js-sdk/pull/1304)
+ * Send the wrong auth params with the right auth params
+ [\#1393](https://github.com/matrix-org/matrix-js-sdk/pull/1393)
+ * encrypt cached keys with pickle key
+ [\#1387](https://github.com/matrix-org/matrix-js-sdk/pull/1387)
+ * Fix replying to key share requests
+ [\#1385](https://github.com/matrix-org/matrix-js-sdk/pull/1385)
+ * Add dist to package.json files so CDNs can serve it
+ [\#1384](https://github.com/matrix-org/matrix-js-sdk/pull/1384)
+ * Fix getVersion warning saying undefined room
+ [\#1382](https://github.com/matrix-org/matrix-js-sdk/pull/1382)
+ * Combine the two places we processed client-level default push rules
+ [\#1379](https://github.com/matrix-org/matrix-js-sdk/pull/1379)
+ * make MAC check robust against unpadded vs padded base64 differences
+ [\#1378](https://github.com/matrix-org/matrix-js-sdk/pull/1378)
+ * Remove key backup format migration
+ [\#1375](https://github.com/matrix-org/matrix-js-sdk/pull/1375)
+ * Add simple browserify browser-matrix.js tests
+ [\#1241](https://github.com/matrix-org/matrix-js-sdk/pull/1241)
+ * support new key agreement method for SAS
+ [\#1376](https://github.com/matrix-org/matrix-js-sdk/pull/1376)
+
+Changes in [6.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.1.0) (2020-05-19)
+================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.1.0-rc.1...v6.1.0)
+
+ * No changes since rc.1
+
+Changes in [6.1.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.1.0-rc.1) (2020-05-14)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0...v6.1.0-rc.1)
+
+ * Remove support for asymmetric 4S encryption
+ [\#1373](https://github.com/matrix-org/matrix-js-sdk/pull/1373)
+ * Increase timeout for 2nd phase of Olm session creation
+ [\#1367](https://github.com/matrix-org/matrix-js-sdk/pull/1367)
+ * Add logging on decryption retries
+ [\#1366](https://github.com/matrix-org/matrix-js-sdk/pull/1366)
+ * Emit event when a trusted self-key is stored
+ [\#1364](https://github.com/matrix-org/matrix-js-sdk/pull/1364)
+ * Customize error payload for oversized messages
+ [\#1352](https://github.com/matrix-org/matrix-js-sdk/pull/1352)
+ * Return null for key backup state when we haven't checked yet
+ [\#1363](https://github.com/matrix-org/matrix-js-sdk/pull/1363)
+ * Added a progressCallback for backup key loading
+ [\#1351](https://github.com/matrix-org/matrix-js-sdk/pull/1351)
+ * Add initialFetch param to willUpdateDevices / devicesUpdated
+ [\#1360](https://github.com/matrix-org/matrix-js-sdk/pull/1360)
+ * Fix race between sending .request and receiving .ready over to_device
+ [\#1359](https://github.com/matrix-org/matrix-js-sdk/pull/1359)
+ * Handle race between sending and await next event from other party
+ [\#1357](https://github.com/matrix-org/matrix-js-sdk/pull/1357)
+ * Add crypto.willUpdateDevices event and make
+ getStoredDevices/getStoredDevicesForUser synchronous
+ [\#1354](https://github.com/matrix-org/matrix-js-sdk/pull/1354)
+ * Fix sender of local echo events in unsigned redactions
+ [\#1350](https://github.com/matrix-org/matrix-js-sdk/pull/1350)
+ * Remove redundant key backup setup path
+ [\#1353](https://github.com/matrix-org/matrix-js-sdk/pull/1353)
+ * Remove some dead code from _retryDecryption
+ [\#1349](https://github.com/matrix-org/matrix-js-sdk/pull/1349)
+ * Don't send key requests until after sync processing is finished
+ [\#1348](https://github.com/matrix-org/matrix-js-sdk/pull/1348)
+ * Prevent attempts to send olm messages to ourselves
+ [\#1346](https://github.com/matrix-org/matrix-js-sdk/pull/1346)
+ * Retry account data upload requests
+ [\#1345](https://github.com/matrix-org/matrix-js-sdk/pull/1345)
+ * Log first known index with megolm session updates
+ [\#1344](https://github.com/matrix-org/matrix-js-sdk/pull/1344)
+ * Prune to_device messages to avoid sending empty messages
+ [\#1343](https://github.com/matrix-org/matrix-js-sdk/pull/1343)
+ * Convert bunch of things to TypeScript
+ [\#1335](https://github.com/matrix-org/matrix-js-sdk/pull/1335)
+ * Add logging when making new Olm sessions
+ [\#1342](https://github.com/matrix-org/matrix-js-sdk/pull/1342)
+ * Fix: handle filter not found
+ [\#1340](https://github.com/matrix-org/matrix-js-sdk/pull/1340)
+ * Make getAccountDataFromServer return null if not found
+ [\#1338](https://github.com/matrix-org/matrix-js-sdk/pull/1338)
+ * Fix setDefaultKeyId to fail if the request fails
+ [\#1336](https://github.com/matrix-org/matrix-js-sdk/pull/1336)
+ * Document setRoomEncryption not modifying room state
+ [\#1328](https://github.com/matrix-org/matrix-js-sdk/pull/1328)
+ * Fix: don't do extra /filter request when enabling lazy loading of members
+ [\#1332](https://github.com/matrix-org/matrix-js-sdk/pull/1332)
+ * Reject attemptAuth promise if no auth flow found
+ [\#1329](https://github.com/matrix-org/matrix-js-sdk/pull/1329)
+ * Fix FilterComponent allowed_values check
+ [\#1327](https://github.com/matrix-org/matrix-js-sdk/pull/1327)
+ * Serialise Olm prekey decryptions
+ [\#1326](https://github.com/matrix-org/matrix-js-sdk/pull/1326)
+ * Fix: crash when backup key needs fixing from corruption issue
+ [\#1324](https://github.com/matrix-org/matrix-js-sdk/pull/1324)
+ * Fix cross-signing/SSSS reset
+ [\#1322](https://github.com/matrix-org/matrix-js-sdk/pull/1322)
+ * Implement QR code reciprocate for self-verification with untrusted MSK
+ [\#1320](https://github.com/matrix-org/matrix-js-sdk/pull/1320)
+
+Changes in [6.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0) (2020-05-05)
+================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0-rc.2...v6.0.0)
+
+ * Add progress callback for key backups
+ [\#1368](https://github.com/matrix-org/matrix-js-sdk/pull/1368)
+
+Changes in [6.0.0-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0-rc.2) (2020-05-01)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.0.0-rc.1...v6.0.0-rc.2)
+
+ * Emit event when a trusted self-key is stored
+ [\#1365](https://github.com/matrix-org/matrix-js-sdk/pull/1365)
+
+Changes in [6.0.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.0.0-rc.1) (2020-04-30)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.4...v6.0.0-rc.1)
+
+BREAKING CHANGES
+---
+
+ * client.getStoredDevicesForUser and client.getStoredDevices are no longer async
+
+All Changes
+---
+
+ * Add initialFetch param to willUpdateDevices / devicesUpdated
+ [\#1362](https://github.com/matrix-org/matrix-js-sdk/pull/1362)
+ * Fix race between sending .request and receiving .ready over to_device
+ [\#1361](https://github.com/matrix-org/matrix-js-sdk/pull/1361)
+ * Handle race between sending and await next event from other party
+ [\#1358](https://github.com/matrix-org/matrix-js-sdk/pull/1358)
+ * Add crypto.willUpdateDevices event and make
+ getStoredDevices/getStoredDevicesForUser synchronous
+ [\#1356](https://github.com/matrix-org/matrix-js-sdk/pull/1356)
+ * Remove redundant key backup setup path
+ [\#1355](https://github.com/matrix-org/matrix-js-sdk/pull/1355)
+
+Changes in [5.3.1-rc.4](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.4) (2020-04-23)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.3...v5.3.1-rc.4)
+
+ * Retry account data upload requests
+ [\#1347](https://github.com/matrix-org/matrix-js-sdk/pull/1347)
+ * Fix: handle filter not found
+ [\#1341](https://github.com/matrix-org/matrix-js-sdk/pull/1341)
+ * Make getAccountDataFromServer return null if not found
+ [\#1339](https://github.com/matrix-org/matrix-js-sdk/pull/1339)
+ * Fix setDefaultKeyId to fail if the request fails
+ [\#1337](https://github.com/matrix-org/matrix-js-sdk/pull/1337)
+ * Fix: don't do extra /filter request when enabling lazy loading of members
+ [\#1333](https://github.com/matrix-org/matrix-js-sdk/pull/1333)
+ * Reject attemptAuth promise if no auth flow found
+ [\#1331](https://github.com/matrix-org/matrix-js-sdk/pull/1331)
+ * Serialise Olm prekey decryptions
+ [\#1330](https://github.com/matrix-org/matrix-js-sdk/pull/1330)
+
+Changes in [5.3.1-rc.3](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.3) (2020-04-17)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.2...v5.3.1-rc.3)
+
+ * Fix cross-signing/SSSS reset
+ [\#1323](https://github.com/matrix-org/matrix-js-sdk/pull/1323)
+ * Fix: crash when backup key needs fixing from corruption issue
+ [\#1325](https://github.com/matrix-org/matrix-js-sdk/pull/1325)
+
+Changes in [5.3.1-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.2) (2020-04-16)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.1-rc.1...v5.3.1-rc.2)
+
+ * Implement QR code reciprocate for self-verification with untrusted MSK
+ [\#1321](https://github.com/matrix-org/matrix-js-sdk/pull/1321)
+
+Changes in [5.3.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.1-rc.1) (2020-04-15)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.3.0-rc.1...v5.3.1-rc.1)
+
+ * Adapt release script for riot-desktop
+ [\#1319](https://github.com/matrix-org/matrix-js-sdk/pull/1319)
+ * Fix: prevent spurious notifications from indexer
+ [\#1318](https://github.com/matrix-org/matrix-js-sdk/pull/1318)
+ * Always create our own user object
+ [\#1317](https://github.com/matrix-org/matrix-js-sdk/pull/1317)
+ * Fix incorrect backup key format in SSSS
+ [\#1311](https://github.com/matrix-org/matrix-js-sdk/pull/1311)
+ * Fix e2ee crash after refreshing after having received a cross-singing key
+ reset
+ [\#1315](https://github.com/matrix-org/matrix-js-sdk/pull/1315)
+ * Fix: catch send errors in SAS verifier
+ [\#1314](https://github.com/matrix-org/matrix-js-sdk/pull/1314)
+ * Clear cross-signing keys when detecting the keys have changed
+ [\#1312](https://github.com/matrix-org/matrix-js-sdk/pull/1312)
+ * Upgrade deps
+ [\#1310](https://github.com/matrix-org/matrix-js-sdk/pull/1310)
+
+Changes in [5.3.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.3.0-rc.1) (2020-04-08)
+==========================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.2.0...v5.3.0-rc.1)
+
+ * Store key backup key in cache as Uint8Array
+ [\#1308](https://github.com/matrix-org/matrix-js-sdk/pull/1308)
+ * Use the correct request body for the /keys/query endpoint.
+ [\#1307](https://github.com/matrix-org/matrix-js-sdk/pull/1307)
+ * Avoid creating two devices on registration
+ [\#1305](https://github.com/matrix-org/matrix-js-sdk/pull/1305)
+ * Lower max-warnings to 81
+ [\#1306](https://github.com/matrix-org/matrix-js-sdk/pull/1306)
+ * Move key backup key creation before caching
+ [\#1303](https://github.com/matrix-org/matrix-js-sdk/pull/1303)
+ * Expose function to force-reset outgoing room key requests
+ [\#1298](https://github.com/matrix-org/matrix-js-sdk/pull/1298)
+ * Add isSelfVerification property to VerificationRequest
+ [\#1302](https://github.com/matrix-org/matrix-js-sdk/pull/1302)
+ * QR code reciprocation
+ [\#1297](https://github.com/matrix-org/matrix-js-sdk/pull/1297)
+ * Add ability to check symmetric SSSS key before we try to use it
+ [\#1294](https://github.com/matrix-org/matrix-js-sdk/pull/1294)
+ * Add some debug logging for events stuck to bottom of timeline
+ [\#1296](https://github.com/matrix-org/matrix-js-sdk/pull/1296)
+ * Fix: spontanous verification request cancellation under some circumstances
+ [\#1295](https://github.com/matrix-org/matrix-js-sdk/pull/1295)
+ * Receive private key for caching from the app layer
+ [\#1293](https://github.com/matrix-org/matrix-js-sdk/pull/1293)
+ * Track whether we have verified a user before
+ [\#1292](https://github.com/matrix-org/matrix-js-sdk/pull/1292)
+ * Fix: error during tests
+ [\#1222](https://github.com/matrix-org/matrix-js-sdk/pull/1222)
+ * Send .done event for to_device verification
+ [\#1288](https://github.com/matrix-org/matrix-js-sdk/pull/1288)
+ * Request the key backup key & restore backup
+ [\#1291](https://github.com/matrix-org/matrix-js-sdk/pull/1291)
+ * Make screen sharing works on Chrome using getDisplayMedia()
+ [\#1276](https://github.com/matrix-org/matrix-js-sdk/pull/1276)
+ * Fix isVerified returning false
+ [\#1289](https://github.com/matrix-org/matrix-js-sdk/pull/1289)
+ * Fix: verification gets cancelled when event gets duplicated
+ [\#1286](https://github.com/matrix-org/matrix-js-sdk/pull/1286)
+ * Use requestSecret on the client to request secrets
+ [\#1287](https://github.com/matrix-org/matrix-js-sdk/pull/1287)
+ * Allow guests to fetch TURN servers
+ [\#1277](https://github.com/matrix-org/matrix-js-sdk/pull/1277)
+
Changes in [5.2.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v5.2.0) (2020-03-30)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v5.2.0-rc.1...v5.2.0)
diff --git a/package.json b/package.json
index 5e7f80bd..f19df47a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
- "version": "5.2.0",
+ "version": "6.2.0",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
@@ -35,6 +35,7 @@
"author": "matrix.org",
"license": "Apache-2.0",
"files": [
+ "dist",
"lib",
"src",
"git-revision.txt",
@@ -68,6 +69,7 @@
"@babel/preset-typescript": "^7.7.4",
"@babel/register": "^7.7.4",
"@types/node": "12",
+ "@types/request": "^2.48.4",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"babelify": "^10.0.0",
diff --git a/release.sh b/release.sh
index 08afb2c2..112da979 100755
--- a/release.sh
+++ b/release.sh
@@ -38,6 +38,7 @@ $USAGE
-c changelog_file: specify name of file containing changelog
-x: skip updating the changelog
-z: skip generating the jsdoc
+ -n: skip publish to NPM
EOF
}
@@ -60,9 +61,10 @@ fi
skip_changelog=
skip_jsdoc=
+skip_npm=
changelog_file="CHANGELOG.md"
expected_npm_user="matrixdotorg"
-while getopts hc:u:xz f; do
+while getopts hc:u:xzn f; do
case $f in
h)
help
@@ -77,6 +79,9 @@ while getopts hc:u:xz f; do
z)
skip_jsdoc=1
;;
+ n)
+ skip_npm=1
+ ;;
u)
expected_npm_user="$OPTARG"
;;
@@ -96,10 +101,12 @@ fi
# Login and publish continues to use `npm`, as it seems to have more clearly
# defined options and semantics than `yarn` for writing to the registry.
-actual_npm_user=`npm whoami`;
-if [ $expected_npm_user != $actual_npm_user ]; then
- echo "you need to be logged into npm as $expected_npm_user, but you are logged in as $actual_npm_user" >&2
- exit 1
+if [ -z "$skip_npm" ]; then
+ actual_npm_user=`npm whoami`;
+ if [ $expected_npm_user != $actual_npm_user ]; then
+ echo "you need to be logged into npm as $expected_npm_user, but you are logged in as $actual_npm_user" >&2
+ exit 1
+ fi
fi
# ignore leading v on release
@@ -298,11 +305,13 @@ rm "${latest_changes}"
# defined options and semantics than `yarn` for writing to the registry.
# Tag both releases and prereleases as `next` so the last stable release remains
# the default.
-npm publish --tag next
-if [ $prerelease -eq 0 ]; then
- # For a release, also add the default `latest` tag.
- package=$(cat package.json | jq -er .name)
- npm dist-tag add "[email protected]$release" latest
+if [ -z "$skip_npm" ]; then
+ npm publish --tag next
+ if [ $prerelease -eq 0 ]; then
+ # For a release, also add the default `latest` tag.
+ package=$(cat package.json | jq -er .name)
+ npm dist-tag add "[email protected]$release" latest
+ fi
fi
if [ -z "$skip_jsdoc" ]; then
@@ -338,8 +347,10 @@ if [ -z "$skip_jsdoc" ]; then
git push origin gh-pages
fi
-# finally, merge master back onto develop
-git checkout develop
-git pull
-git merge master
-git push origin develop
+# finally, merge master back onto develop (if it exists)
+if [ $(git branch -lr | grep origin/develop -c) -ge 1 ]; then
+ git checkout develop
+ git pull
+ git merge master
+ git push origin develop
+fi
diff --git a/spec/browserify/setupTests.js b/spec/browserify/setupTests.js
new file mode 100644
index 00000000..16120f78
--- /dev/null
+++ b/spec/browserify/setupTests.js
@@ -0,0 +1,23 @@
+/*
+Copyright 2020 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.
+*/
+
+// stub for browser-matrix browserify tests
+global.XMLHttpRequest = jest.fn();
+
+afterAll(() => {
+ // clean up XMLHttpRequest mock
+ global.XMLHttpRequest = undefined;
+});
diff --git a/spec/browserify/sync-browserify.spec.js b/spec/browserify/sync-browserify.spec.js
new file mode 100644
index 00000000..c2054d7f
--- /dev/null
+++ b/spec/browserify/sync-browserify.spec.js
@@ -0,0 +1,103 @@
+/*
+Copyright 2020 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.
+*/
+
+// load XmlHttpRequest mock
+import "./setupTests";
+import "../../dist/browser-matrix"; // uses browser-matrix instead of the src
+import {MockStorageApi} from "../MockStorageApi";
+import {WebStorageSessionStore} from "../../src/store/session/webstorage";
+import MockHttpBackend from "matrix-mock-request";
+import {LocalStorageCryptoStore} from "../../src/crypto/store/localStorage-crypto-store";
+import * as utils from "../test-utils";
+
+const USER_ID = "@user:test.server";
+const DEVICE_ID = "device_id";
+const ACCESS_TOKEN = "access_token";
+const ROOM_ID = "!room_id:server.test";
+
+/* global matrixcs */
+
+describe("Browserify Test", function() {
+ let client;
+ let httpBackend;
+
+ async function createTestClient() {
+ const sessionStoreBackend = new MockStorageApi();
+ const sessionStore = new WebStorageSessionStore(sessionStoreBackend);
+ const httpBackend = new MockHttpBackend();
+
+ const options = {
+ baseUrl: "http://" + USER_ID + ".test.server",
+ userId: USER_ID,
+ accessToken: ACCESS_TOKEN,
+ deviceId: DEVICE_ID,
+ sessionStore: sessionStore,
+ request: httpBackend.requestFn,
+ cryptoStore: new LocalStorageCryptoStore(sessionStoreBackend),
+ };
+
+ const client = matrixcs.createClient(options);
+
+ httpBackend.when("GET", "/pushrules").respond(200, {});
+ httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
+
+ return { client, httpBackend };
+ }
+
+ beforeEach(async () => {
+ ({client, httpBackend} = await createTestClient());
+ await client.startClient();
+ });
+
+ afterEach(async () => {
+ client.stopClient();
+ await httpBackend.stop();
+ });
+
+ it("Sync", async function() {
+ const event = utils.mkMembership({
+ room: ROOM_ID,
+ mship: "join",
+ user: "@other_user:server.test",
+ name: "Displayname",
+ });
+
+ const syncData = {
+ next_batch: "batch1",
+ rooms: {
+ join: {},
+ },
+ };
+ syncData.rooms.join[ROOM_ID] = {
+ timeline: {
+ events: [
+ event,
+ ],
+ limited: false,
+ },
+ };
+
+ httpBackend.when("GET", "/sync").respond(200, syncData);
+ await Promise.race([
+ Promise.all([
+ httpBackend.flushAllExpected(),
+ ]),
+ new Promise((_, reject) => {
+ client.once("sync.unexpectedError", reject);
+ }),
+ ]);
+ }, 10000);
+});
diff --git a/spec/integ/matrix-client-opts.spec.js b/spec/integ/matrix-client-opts.spec.js
index 606674e4..7c256fd5 100644
--- a/spec/integ/matrix-client-opts.spec.js
+++ b/spec/integ/matrix-client-opts.spec.js
@@ -3,6 +3,7 @@ import HttpBackend from "matrix-mock-request";
import {MatrixClient} from "../../src/matrix";
import {MatrixScheduler} from "../../src/scheduler";
import {MemoryStore} from "../../src/store/memory";
+import {MatrixError} from "../../src/http-api";
describe("MatrixClient opts", function() {
const baseUrl = "http://localhost.or.something";
@@ -132,10 +133,10 @@ describe("MatrixClient opts", function() {
});
it("shouldn't retry sending events", function(done) {
- httpBackend.when("PUT", "/txn1").fail(500, {
+ httpBackend.when("PUT", "/txn1").fail(500, new MatrixError({
errcode: "M_SOMETHING",
error: "Ruh roh",
- });
+ }));
client.sendTextMessage("!foo:bar", "a body", "txn1").then(function(res) {
expect(false).toBe(true, "sendTextMessage resolved but shouldn't");
}, function(err) {
diff --git a/spec/olm-loader.js b/spec/olm-loader.js
index b06ecdff..22f049ae 100644
--- a/spec/olm-loader.js
+++ b/spec/olm-loader.js
@@ -16,6 +16,7 @@ limitations under the License.
*/
import {logger} from '../src/logger';
+import * as utils from "../src/utils";
// try to load the olm library.
try {
@@ -24,3 +25,11 @@ try {
} catch (e) {
logger.warn("unable to run crypto tests: libolm not available");
}
+
+// also try to set node crypto
+try {
+ const crypto = require('crypto');
+ utils.setCrypto(crypto);
+} catch (err) {
+ console.log('nodejs was compiled without crypto support: some tests will fail');
+}
diff --git a/spec/unit/crypto.spec.js b/spec/unit/crypto.spec.js
index 9652f4b3..18cc930b 100644
--- a/spec/unit/crypto.spec.js
+++ b/spec/unit/crypto.spec.js
@@ -313,6 +313,10 @@ describe("Crypto", function() {
// make a room key request, and record the transaction ID for the
// sendToDevice call
await aliceClient.cancelAndResendEventRoomKeyRequest(event);
+ // key requests get queued until the sync has finished, but we don't
+ // let the client set up enough for that to happen, so gut-wrench a bit
+ // to force it to send now.
+ aliceClient._crypto._outgoingRoomKeyRequestManager.sendQueuedRequests();
jest.runAllTimers();
await Promise.resolve();
expect(aliceClient.sendToDevice).toBeCalledTimes(1);
diff --git a/spec/unit/crypto/CrossSigningInfo.spec.js b/spec/unit/crypto/CrossSigningInfo.spec.js
index c49451ed..4c746147 100644
--- a/spec/unit/crypto/CrossSigningInfo.spec.js
+++ b/spec/unit/crypto/CrossSigningInfo.spec.js
@@ -25,6 +25,7 @@ import {
import {MemoryCryptoStore} from '../../../src/crypto/store/memory-crypto-store';
import 'fake-indexeddb/auto';
import 'jest-localstorage-mock';
+import {OlmDevice} from "../../../src/crypto/OlmDevice";
const userId = "@alice:example.com";
@@ -233,8 +234,9 @@ describe.each([
it("should cache data to the store and retrieve it", async () => {
await store.startup();
+ const olmDevice = new OlmDevice(store);
const { getCrossSigningKeyCache, storeCrossSigningKeyCache } =
- createCryptoStoreCacheCallbacks(store);
+ createCryptoStoreCacheCallbacks(store, olmDevice);
await storeCrossSigningKeyCache("self_signing", testKey);
// If we've not saved anything, don't expect anything
@@ -243,6 +245,6 @@ describe.each([
expect(nokey).toBeNull();
const key = await getCrossSigningKeyCache("self_signing", "");
- expect(key).toEqual(testKey);
+ expect(new Uint8Array(key)).toEqual(testKey);
});
});
diff --git a/spec/unit/crypto/backup.spec.js b/spec/unit/crypto/backup.spec.js
index a7ee344a..e03460fe 100644
--- a/spec/unit/crypto/backup.spec.js
+++ b/spec/unit/crypto/backup.spec.js
@@ -546,7 +546,7 @@ describe("MegolmBackup", function() {
const key = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8]);
await client._crypto.storeSessionBackupPrivateKey(key);
const result = await client._crypto.getSessionBackupPrivateKey();
- expect(result).toEqual(key);
+ expect(new Uint8Array(result)).toEqual(key);
});
it('caches session backup keys as it encounters them', async function() {
diff --git a/spec/unit/crypto/secrets.spec.js b/spec/unit/crypto/secrets.spec.js
index 0a045935..74552a2e 100644
--- a/spec/unit/crypto/secrets.spec.js
+++ b/spec/unit/crypto/secrets.spec.js
@@ -49,6 +49,13 @@ async function makeTestClient(userInfo, options) {
return client;
}
+// Wrapper around pkSign to return a signed object. pkSign returns the
+// signature, rather than the signed object.
+function sign(obj, key, userId) {
+ olmlib.pkSign(obj, key, userId);
+ return obj;
+}
+
describe("Secrets", function() {
if (!global.Olm) {
console.warn('Not running megolm backup unit tests: libolm not present');
@@ -266,104 +273,259 @@ describe("Secrets", function() {
expect(secret).toBe("bar");
});
- it("bootstraps when no storage or cross-signing keys locally", async function() {
- const key = new Uint8Array(16);
- for (let i = 0; i < 16; i++) key[i] = i;
- const getKey = jest.fn(e => {
- return [Object.keys(e.keys)[0], key];
- });
-
- const bob = await makeTestClient(
- {
- userId: "@bob:example.com",
- deviceId: "bob1",
- },
- {
- cryptoCallbacks: {
- getSecretStorageKey: getKey,
- },
- },
+ describe("bootstrap", function() {
+ // keys used in some of the tests
+ const XSK = new Uint8Array(
+ olmlib.decodeBase64("3lo2YdJugHjfE+Or7KJ47NuKbhE7AAGLgQ/dc19913Q="),
+ );
+ const XSPubKey = "DRb8pFVJyEJ9OWvXeUoM0jq/C2Wt+NxzBZVuk2nRb+0";
+ const USK = new Uint8Array(
+ olmlib.decodeBase64("lKWi3hJGUie5xxHgySoz8PHFnZv6wvNaud/p2shN9VU="),
+ );
+ const USPubKey = "CUpoiTtHiyXpUmd+3ohb7JVxAlUaOG1NYs9Jlx8soQU";
+ const SSK = new Uint8Array(
+ olmlib.decodeBase64("1R6JVlXX99UcfUZzKuCDGQgJTw8ur1/ofgPD8pp+96M="),
+ );
+ const SSPubKey = "0DfNsRDzEvkCLA0gD3m7VAGJ5VClhjEsewI35xq873Q";
+ const SSSSKey = new Uint8Array(
+ olmlib.decodeBase64(
+ "XrmITOOdBhw6yY5Bh7trb/bgp1FRdIGyCUxxMP873R0=",
+ ),
);
- bob.uploadDeviceSigningKeys = async () => {};
- bob.uploadKeySignatures = async () => {};
- bob.setAccountData = async function(eventType, contents, callback) {
- const event = new MatrixEvent({
- type: eventType,
- content: contents,
- });
- this.store.storeAccountDataEvents([
- event,
- ]);
- this.emit("accountData", event);
- };
-
- await bob.bootstrapSecretStorage();
- const crossSigning = bob._crypto._crossSigningInfo;
- const secretStorage = bob._crypto._secretStorage;
+ it("bootstraps when no storage or cross-signing keys locally", async function() {
+ const key = new Uint8Array(16);
+ for (let i = 0; i < 16; i++) key[i] = i;
+ const getKey = jest.fn(e => {
+ return [Object.keys(e.keys)[0], key];
+ });
- expect(crossSigning.getId()).toBeTruthy();
- expect(await crossSigning.isStoredInSecretStorage(secretStorage)).toBeTruthy();
- expect(await secretStorage.hasKey()).toBeTruthy();
- });
+ const bob = await makeTestClient(
+ {
+ userId: "@bob:example.com",
+ deviceId: "bob1",
+ },
+ {
+ cryptoCallbacks: {
+ getSecretStorageKey: getKey,
+ },
+ },
+ );
+ bob.uploadDeviceSigningKeys = async () => {};
+ bob.uploadKeySignatures = async () => {};
+ bob.setAccountData = async function(eventType, contents, callback) {
+ const event = new MatrixEvent({
+ type: eventType,
+ content: contents,
+ });
+ this.store.storeAccountDataEvents([
+ event,
+ ]);
+ this.emit("accountData", event);
+ };
+
+ await bob.bootstrapSecretStorage();
+
+ const crossSigning = bob._crypto._crossSigningInfo;
+ const secretStorage = bob._crypto._secretStorage;
+
+ expect(crossSigning.getId()).toBeTruthy();
+ expect(await crossSigning.isStoredInSecretStorage(secretStorage))
+ .toBeTruthy();
+ expect(await secretStorage.hasKey()).toBeTruthy();
+ });
- it("bootstraps when cross-signing keys in secret storage", async function() {
- const decryption = new global.Olm.PkDecryption();
- const storagePublicKey = decryption.generate_key();
- const storagePrivateKey = decryption.get_private_key();
+ it("bootstraps when cross-signing keys in secret storage", async function() {
+ const decryption = new global.Olm.PkDecryption();
+ const storagePublicKey = decryption.generate_key();
+ const storagePrivateKey = decryption.get_private_key();
- const bob = await makeTestClient(
- {
- userId: "@bob:example.com",
- deviceId: "bob1",
- },
- {
- cryptoCallbacks: {
- getSecretStorageKey: async request => {
- const defaultKeyId = await bob.getDefaultSecretStorageKeyId();
- expect(Object.keys(request.keys)).toEqual([defaultKeyId]);
- return [defaultKeyId, storagePrivateKey];
+ const bob = await makeTestClient(
+ {
+ userId: "@bob:example.com",
+ deviceId: "bob1",
+ },
+ {
+ cryptoCallbacks: {
+ getSecretStorageKey: async request => {
+ const defaultKeyId = await bob.getDefaultSecretStorageKeyId();
+ expect(Object.keys(request.keys)).toEqual([defaultKeyId]);
+ return [defaultKeyId, storagePrivateKey];
+ },
},
},
- },
- );
+ );
- bob.uploadDeviceSigningKeys = async () => {};
- bob.uploadKeySignatures = async () => {};
- bob.setAccountData = async function(eventType, contents, callback) {
- const event = new MatrixEvent({
- type: eventType,
- content: contents,
+ bob.uploadDeviceSigningKeys = async () => {};
+ bob.uploadKeySignatures = async () => {};
+ bob.setAccountData = async function(eventType, contents, callback) {
+ const event = new MatrixEvent({
+ type: eventType,
+ content: contents,
+ });
+ this.store.storeAccountDataEvents([
+ event,
+ ]);
+ this.emit("accountData", event);
+ };
+ bob._crypto.checkKeyBackup = async () => {};
+
+ const crossSigning = bob._crypto._crossSigningInfo;
+ const secretStorage = bob._crypto._secretStorage;
+
+ // Set up cross-signing keys from scratch with specific storage key
+ await bob.bootstrapSecretStorage({
+ createSecretStorageKey: async () => ({
+ // `pubkey` not used anymore with symmetric 4S
+ keyInfo: { pubkey: storagePublicKey },
+ privateKey: storagePrivateKey,
+ }),
});
- this.store.storeAccountDataEvents([
- event,
- ]);
- this.emit("accountData", event);
- };
- bob._crypto.checkKeyBackup = async () => {};
- const crossSigning = bob._crypto._crossSigningInfo;
- const secretStorage = bob._crypto._secretStorage;
-
- // Set up cross-signing keys from scratch with specific storage key
- await bob.bootstrapSecretStorage({
- createSecretStorageKey: async () => ({
- // `pubkey` not used anymore with symmetric 4S
- keyInfo: { pubkey: storagePublicKey },
- privateKey: storagePrivateKey,
- }),
+ // Clear local cross-signing keys and read from secret storage
+ bob._crypto._deviceList.storeCrossSigningForUser(
+ "@bob:example.com",
+ crossSigning.toStorage(),
+ );
+ crossSigning.keys = {};
+ await bob.bootstrapSecretStorage();
+
+ expect(crossSigning.getId()).toBeTruthy();
+ expect(await crossSigning.isStoredInSecretStorage(secretStorage))
+ .toBeTruthy();
+ expect(await secretStorage.hasKey()).toBeTruthy();
});
- // Clear local cross-signing keys and read from secret storage
- bob._crypto._deviceList.storeCrossSigningForUser(
- "@bob:example.com",
- crossSigning.toStorage(),
- );
- crossSigning.keys = {};
- await bob.bootstrapSecretStorage();
-
- expect(crossSigning.getId()).toBeTruthy();
- expect(await crossSigning.isStoredInSecretStorage(secretStorage)).toBeTruthy();
- expect(await secretStorage.hasKey()).toBeTruthy();
+ it("adds passphrase checking if it's lacking", async function() {
+ let crossSigningKeys = {
+ master: XSK,
+ user_signing: USK,
+ self_signing: SSK,
+ };
+ const secretStorageKeys = {
+ key_id: SSSSKey,
+ };
+ const alice = await makeTestClient(
+ {userId: "@alice:example.com", deviceId: "Osborne2"},
+ {
+ cryptoCallbacks: {
+ getCrossSigningKey: t => crossSigningKeys[t],
+ saveCrossSigningKeys: k => crossSigningKeys = k,
+ getSecretStorageKey: ({keys}, name) => {
+ for (const keyId of Object.keys(keys)) {
+ if (secretStorageKeys[keyId]) {
+ return [keyId, secretStorageKeys[keyId]];
+ }
+ }
+ },
+ },
+ },
+ );
+ alice.store.storeAccountDataEvents([
+ new MatrixEvent({
+ type: "m.secret_storage.default_key",
+ content: {
+ key: "key_id",
+ },
+ }),
+ new MatrixEvent({
+ type: "m.secret_storage.key.key_id",
+ content: {
+ algorithm: "m.secret_storage.v1.aes-hmac-sha2",
+ passphrase: {
+ algorithm: "m.pbkdf2",
+ iterations: 500000,
+ salt: "GbkvwKHVMveo1zGVSb2GMMdCinG2npJK",
+ },
+ },
+ }),
+ // we never use these values, other than checking that they
+ // exist, so just use dummy values
+ new MatrixEvent({
+ type: "m.cross_signing.master",
+ content: {
+ encrypted: {
+ key_id: {ciphertext: "bla", mac: "bla", iv: "bla"},
+ },
+ },
+ }),
+ new MatrixEvent({
+ type: "m.cross_signing.self_signing",
+ content: {
+ encrypted: {
+ key_id: {ciphertext: "bla", mac: "bla", iv: "bla"},
+ },
+ },
+ }),
+ new MatrixEvent({
+ type: "m.cross_signing.user_signing",
+ content: {
+ encrypted: {
+ key_id: {ciphertext: "bla", mac: "bla", iv: "bla"},
+ },
+ },
+ }),
+ ]);
+ alice._crypto._deviceList.storeCrossSigningForUser("@alice:example.com", {
+ keys: {
+ master: {
+ user_id: "@alice:example.com",
+ usage: ["master"],
+ keys: {
+ [`ed25519:${XSPubKey}`]: XSPubKey,
+ },
+ },
+ self_signing: sign({
+ user_id: "@alice:example.com",
+ usage: ["self_signing"],
+ keys: {
+ [`ed25519:${SSPubKey}`]: SSPubKey,
+ },
+ }, XSK, "@alice:example.com"),
+ user_signing: sign({
+ user_id: "@alice:example.com",
+ usage: ["user_signing"],
+ keys: {
+ [`ed25519:${USPubKey}`]: USPubKey,
+ },
+ }, XSK, "@alice:example.com"),
+ },
+ });
+ alice.getKeyBackupVersion = async () => {
+ return {
+ version: "1",
+ algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
+ auth_data: sign({
+ public_key: "pxEXhg+4vdMf/kFwP4bVawFWdb0EmytL3eFJx++zQ0A",
+ }, XSK, "@alice:example.com"),
+ };
+ };
+ alice.setAccountData = async function(name, data) {
+ const event = new MatrixEvent({
+ type: name,
+ content: data,
+ });
+ alice.store.storeAccountDataEvents([event]);
+ this.emit("accountData", event);
+ };
+
+ await alice.bootstrapSecretStorage();
+
+ expect(alice.getAccountData("m.secret_storage.default_key").getContent())
+ .toEqual({key: "key_id"});
+ const keyInfo = alice.getAccountData("m.secret_storage.key.key_id")
+ .getContent();
+ expect(keyInfo.algorithm)
+ .toEqual("m.secret_storage.v1.aes-hmac-sha2");
+ expect(keyInfo.passphrase).toEqual({
+ algorithm: "m.pbkdf2",
+ iterations: 500000,
+ salt: "GbkvwKHVMveo1zGVSb2GMMdCinG2npJK",
+ });
+ expect(keyInfo).toHaveProperty("iv");
+ expect(keyInfo).toHaveProperty("mac");
+ expect(alice.checkSecretStorageKey(secretStorageKeys.key_id, keyInfo))
+ .toBeTruthy();
+ });
});
});
diff --git a/spec/unit/crypto/verification/sas.spec.js b/spec/unit/crypto/verification/sas.spec.js
index bea6c450..88f424fe 100644
--- a/spec/unit/crypto/verification/sas.spec.js
+++ b/spec/unit/crypto/verification/sas.spec.js
@@ -44,7 +44,16 @@ describe("SAS verification", function() {
});
it("should error on an unexpected event", async function() {
- const sas = new SAS({}, "@alice:example.com", "ABCDEFG");
+ //channel, baseApis, userId, deviceId, startEvent, request
+ const request = {
+ onVerifierCancelled: function() {},
+ };
+ const channel = {
+ send: function() {
+ return Promise.resolve();
+ },
+ };
+ const sas = new SAS(channel, {}, "@alice:example.com", "ABCDEFG", null, request);
sas.handleEvent(new MatrixEvent({
sender: "@alice:example.com",
type: "es.inquisition",
@@ -172,11 +181,14 @@ describe("SAS verification", function() {
it("should verify a key", async () => {
let macMethod;
+ let keyAgreement;
const origSendToDevice = bob.client.sendToDevice.bind(bob.client);
bob.client.sendToDevice = function(type, map) {
if (type === "m.key.verification.accept") {
macMethod = map[alice.client.getUserId()][alice.client.deviceId]
.message_authentication_code;
+ keyAgreement = map[alice.client.getUserId()][alice.client.deviceId]
+ .key_agreement_protocol;
}
return origSendToDevice(type, map);
};
@@ -203,6 +215,7 @@ describe("SAS verification", function() {
// make sure that it uses the preferred method
expect(macMethod).toBe("hkdf-hmac-sha256");
+ expect(keyAgreement).toBe("curve25519-hkdf-sha256");
// make sure Alice and Bob verified each other
const bobDevice
diff --git a/spec/unit/crypto/verification/verification_request.spec.js b/spec/unit/crypto/verification/verification_request.spec.js
index ac9b0f8d..a90d0482 100644
--- a/spec/unit/crypto/verification/verification_request.spec.js
+++ b/spec/unit/crypto/verification/verification_request.spec.js
@@ -119,6 +119,8 @@ async function distributeEvent(ownRequest, theirRequest, event) {
await theirRequest.channel.handleEvent(event, theirRequest, true);
}
+jest.useFakeTimers();
+
describe("verification request unit tests", function() {
beforeAll(function() {
setupWebcrypto();
@@ -246,4 +248,38 @@ describe("verification request unit tests", function() {
expect(bob1Request.done).toBe(true);
expect(bob2Request.done).toBe(true);
});
+
+ it("request times out after 10 minutes", async function() {
+ const alice = makeMockClient("@alice:matrix.tld", "device1");
+ const bob = makeMockClient("@bob:matrix.tld", "device1");
+ const aliceRequest = new VerificationRequest(
+ new InRoomChannel(alice, "!room", bob.getUserId()), new Map(), alice);
+ await aliceRequest.sendRequest();
+ const [requestEvent] = alice.popEvents();
+ await aliceRequest.channel.handleEvent(requestEvent, aliceRequest, true,
+ true, true);
+
+ expect(aliceRequest.cancelled).toBe(false);
+ expect(aliceRequest._cancellingUserId).toBe(undefined);
+ jest.advanceTimersByTime(10 * 60 * 1000);
+ expect(aliceRequest._cancellingUserId).toBe(alice.getUserId());
+ });
+
+ it("request times out 2 minutes after receipt", async function() {
+ const alice = makeMockClient("@alice:matrix.tld", "device1");
+ const bob = makeMockClient("@bob:matrix.tld", "device1");
+ const aliceRequest = new VerificationRequest(
+ new InRoomChannel(alice, "!room", bob.getUserId()), new Map(), alice);
+ await aliceRequest.sendRequest();
+ const [requestEvent] = alice.popEvents();
+ const bobRequest = new VerificationRequest(
+ new InRoomChannel(bob, "!room"), new Map(), bob);
+
+ await bobRequest.channel.handleEvent(requestEvent, bobRequest, true);
+
+ expect(bobRequest.cancelled).toBe(false);
+ expect(bobRequest._cancellingUserId).toBe(undefined);
+ jest.advanceTimersByTime(2 * 60 * 1000);
+ expect(bobRequest._cancellingUserId).toBe(bob.getUserId());
+ });
});
diff --git a/spec/unit/filter-component.spec.js b/spec/unit/filter-component.spec.js
new file mode 100644
index 00000000..9f43b15f
--- /dev/null
+++ b/spec/unit/filter-component.spec.js
@@ -0,0 +1,34 @@
+import {FilterComponent} from "../../src/filter-component";
+import {mkEvent} from '../test-utils';
+
+describe("Filter Component", function() {
+ describe("types", function() {
+ it("should filter out events with other types", function() {
+ const filter = new FilterComponent({ types: ['m.room.message'] });
+ const event = mkEvent({
+ type: 'm.room.member',
+ content: { },
+ room: 'roomId',
+ event: true,
+ });
+
+ const checkResult = filter.check(event);
+
+ expect(checkResult).toBe(false);
+ });
+
+ it("should validate events with the same type", function() {
+ const filter = new FilterComponent({ types: ['m.room.message'] });
+ const event = mkEvent({
+ type: 'm.room.message',
+ content: { },
+ room: 'roomId',
+ event: true,
+ });
+
+ const checkResult = filter.check(event);
+
+ expect(checkResult).toBe(true);
+ });
+ });
+});
diff --git a/spec/unit/interactive-auth.spec.js b/spec/unit/interactive-auth.spec.js
index 46008b90..609d21eb 100644
--- a/spec/unit/interactive-auth.spec.js
+++ b/spec/unit/interactive-auth.spec.js
@@ -97,7 +97,7 @@ describe("InteractiveAuth", function() {
// first we expect a call to doRequest
doRequest.mockImplementation(function(authData) {
logger.log("request1", authData);
- expect(authData).toEqual({});
+ expect(authData).toEqual(null); // first request should be null
const err = new MatrixError({
session: "sessionId",
flows: [
@@ -143,4 +143,33 @@ describe("InteractiveAuth", function() {
expect(stateUpdated).toBeCalledTimes(1);
});
});
+
+ it("should start an auth stage and reject if no auth flow", function() {
+ const doRequest = jest.fn();
+ const stateUpdated = jest.fn();
+
+ const ia = new InteractiveAuth({
+ matrixClient: new FakeClient(),
+ doRequest: doRequest,
+ stateUpdated: stateUpdated,
+ });
+
+ doRequest.mockImplementation(function(authData) {
+ logger.log("request1", authData);
+ expect(authData).toEqual(null); // first request should be null
+ const err = new MatrixError({
+ session: "sessionId",
+ flows: [],
+ params: {
+ "logintype": { param: "aa" },
+ },
+ });
+ err.httpStatus = 401;
+ throw err;
+ });
+
+ return ia.attemptAuth().catch(function(error) {
+ expect(error.message).toBe('No appropriate authentication flow found');
+ });
+ });
});
diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts
new file mode 100644
index 00000000..c8a13144
--- /dev/null
+++ b/src/@types/global.d.ts
@@ -0,0 +1,25 @@
+/*
+Copyright 2020 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.
+*/
+
+export {};
+
+declare global {
+ namespace NodeJS {
+ interface Global {
+ localStorage: Storage;
+ }
+ }
+}
diff --git a/src/base-apis.js b/src/base-apis.js
index ce74ce8e..4b6a77b6 100644
--- a/src/base-apis.js
+++ b/src/base-apis.js
@@ -206,9 +206,6 @@ MatrixBaseApis.prototype.register = function(
inhibitLogin = undefined;
}
- if (auth === undefined || auth === null) {
- auth = {};
- }
if (sessionId) {
auth.session = sessionId;
}
diff --git a/src/client.js b/src/client.js
index 64174c99..d390441e 100644
--- a/src/client.js
+++ b/src/client.js
@@ -35,7 +35,12 @@ import {StubStore} from "./store/stub";
import {createNewMatrixCall} from "./webrtc/call";
import * as utils from './utils';
import {sleep} from './utils';
-import {MatrixError, PREFIX_MEDIA_R0, PREFIX_UNSTABLE} from "./http-api";
+import {
+ MatrixError,
+ PREFIX_MEDIA_R0,
+ PREFIX_UNSTABLE,
+ retryNetworkOperation,
+} from "./http-api";
import {getHttpUriForMxc} from "./content-repo";
import * as ContentHelpers from "./content-helpers";
import * as olmlib from "./crypto/olmlib";
@@ -48,6 +53,8 @@ import {keyFromAuthData} from './crypto/key_passphrase';
import {randomString} from './randomstring';
import {PushProcessor} from "./pushprocessor";
import {encodeBase64, decodeBase64} from "./crypto/olmlib";
+import { User } from "./models/user";
+import {AutoDiscovery} from "./autodiscovery";
const SCROLLBACK_DELAY_MS = 3000;
export const CRYPTO_ENABLED = isCryptoAvailable();
@@ -104,6 +111,9 @@ function keyFromRecoverySession(session, decryptionKey) {
* If provided, opts.deviceId and opts.userId should **NOT** be provided
* (they are present in the exported data).
*
+ * @param {string} opts.pickleKey Key used to pickle olm objects or other
+ * sensitive data.
+ *
* @param {IdentityServerProvider} [opts.identityServer]
* Optional. A provider object with one function `getAccessToken`, which is a
* callback that returns a Promise<String> of an identity access token to supply
@@ -278,6 +288,8 @@ export function MatrixClient(opts) {
// will be used during async initialization of the crypto
this._exportedOlmDeviceToImport = opts.deviceToImport.olmDevice;
}
+ } else if (opts.pickleKey) {
+ this.pickleKey = opts.pickleKey;
}
this.scheduler = opts.scheduler;
@@ -318,7 +330,7 @@ export function MatrixClient(opts) {
this._isGuest = false;
this._ongoingScrollbacks = {};
this.timelineSupport = Boolean(opts.timelineSupport);
- this.urlPreviewCache = {};
+ this.urlPreviewCache = {}; // key=preview key, value=Promise for preview (may be an error)
this._notifTimelineSet = null;
this.unstableClientRelationAggregation = !!opts.unstableClientRelationAggregation;
@@ -731,6 +743,7 @@ MatrixClient.prototype.initCrypto = async function() {
"crypto.roomKeyRequestCancellation",
"crypto.warning",
"crypto.devicesUpdated",
+ "crypto.willUpdateDevices",
"deviceVerificationChanged",
"userTrustStatusChanged",
"crossSigning.keysChanged",
@@ -739,6 +752,7 @@ MatrixClient.prototype.initCrypto = async function() {
logger.log("Crypto: initialising crypto object...");
await crypto.init({
exportedOlmDevice: this._exportedOlmDeviceToImport,
+ pickleKey: this.pickleKey,
});
delete this._exportedOlmDeviceToImport;
@@ -819,9 +833,9 @@ MatrixClient.prototype.downloadKeys = function(userIds, forceDownload) {
*
* @param {string} userId the user to list keys for.
*
- * @return {Promise<module:crypto/deviceinfo[]>} list of devices
+ * @return {module:crypto/deviceinfo[]} list of devices
*/
-MatrixClient.prototype.getStoredDevicesForUser = async function(userId) {
+MatrixClient.prototype.getStoredDevicesForUser = function(userId) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
@@ -834,9 +848,9 @@ MatrixClient.prototype.getStoredDevicesForUser = async function(userId) {
* @param {string} userId the user to list keys for.
* @param {string} deviceId unique identifier for the device
*
- * @return {Promise<?module:crypto/deviceinfo>} device or null
+ * @return {module:crypto/deviceinfo} device or null
*/
-MatrixClient.prototype.getStoredDevice = async function(userId, deviceId) {
+MatrixClient.prototype.getStoredDevice = function(userId, deviceId) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
@@ -1303,7 +1317,6 @@ wrapCryptoFuncs(MatrixClient, [
"bootstrapSecretStorage",
"addSecretStorageKey",
"hasSecretStorageKey",
- "secretStorageKeyNeedsUpgrade",
"storeSecret",
"getSecret",
"isSecretStored",
@@ -1357,7 +1370,8 @@ MatrixClient.prototype.cancelAndResendEventRoomKeyRequest = function(event) {
};
/**
- * Enable end-to-end encryption for a room.
+ * Enable end-to-end encryption for a room. This does not modify room state.
+ * Any messages sent before the returned promise resolves will be sent unencrypted.
* @param {string} roomId The room ID to enable encryption in.
* @param {object} config The encryption config for the room.
* @return {Promise} A promise that will resolve when encryption is set up.
@@ -1429,15 +1443,17 @@ MatrixClient.prototype.exportRoomKeys = function() {
* Import a list of room keys previously exported by exportRoomKeys
*
* @param {Object[]} keys a list of session export objects
+ * @param {Object} opts
+ * @param {Function} opts.progressCallback called with an object that has a "stage" param
*
* @return {Promise} a promise which resolves when the keys
* have been imported
*/
-MatrixClient.prototype.importRoomKeys = function(keys) {
+MatrixClient.prototype.importRoomKeys = function(keys, opts) {
if (!this._crypto) {
throw new Error("End-to-end encryption disabled");
}
- return this._crypto.importRoomKeys(keys);
+ return this._crypto.importRoomKeys(keys, opts);
};
/**
@@ -1497,12 +1513,16 @@ MatrixClient.prototype.isKeyBackupTrusted = function(info) {
/**
* @returns {bool} true if the client is configured to back up keys to
- * the server, otherwise false.
+ * the server, otherwise false. If we haven't completed a successful check
+ * of key backup status yet, returns null.
*/
MatrixClient.prototype.getKeyBackupEnabled = function() {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
}
+ if (!this._crypto._checkedForBackup) {
+ return null;
+ }
return Boolean(this._crypto.backupKey);
};
@@ -1870,7 +1890,10 @@ MatrixClient.prototype.restoreKeyBackupWithCache = async function(
MatrixClient.prototype._restoreKeyBackup = function(
privKey, targetRoomId, targetSessionId, backupInfo,
- { cacheCompleteCallback }={}, // For sequencing during tests
+ {
+ cacheCompleteCallback, // For sequencing during tests
+ progressCallback,
+ }={},
) {
if (this._crypto === null) {
throw new Error("End-to-end encryption disabled");
@@ -1905,6 +1928,12 @@ MatrixClient.prototype._restoreKeyBackup = function(
console.warn("Error caching session backup key:", e);
}).then(cacheCompleteCallback);
+ if (progressCallback) {
+ progressCallback({
+ stage: "fetch",
+ });
+ }
+
return this._http.authedRequest(
undefined, "GET", path.path, path.queryData, undefined,
{prefix: PREFIX_UNSTABLE},
@@ -1939,7 +1968,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
}
}
- return this.importRoomKeys(keys);
+ return this.importRoomKeys(keys, { progressCallback });
}).then(() => {
return this._crypto.setTrustedBackupPubKey(backupPubKey);
}).then(() => {
@@ -2075,6 +2104,7 @@ MatrixClient.prototype.getUsers = function() {
/**
* Set account data event for the current user.
+ * It will retry the request up to 5 times.
* @param {string} eventType The event type
* @param {Object} contents the contents object for the event
* @param {module:client.callback} callback Optional.
@@ -2086,9 +2116,13 @@ MatrixClient.prototype.setAccountData = function(eventType, contents, callback)
$userId: this.credentials.userId,
$type: eventType,
});
- return this._http.authedRequest(
- callback, "PUT", path, undefined, contents,
- );
+ const promise = retryNetworkOperation(5, () => {
+ return this._http.authedRequest(undefined, "PUT", path, undefined, contents);
+ });
+ if (callback) {
+ promise.then(result => callback(null, result), callback);
+ }
+ return promise;
};
/**
@@ -2123,9 +2157,17 @@ MatrixClient.prototype.getAccountDataFromServer = async function(eventType) {
$userId: this.credentials.userId,
$type: eventType,
});
- return this._http.authedRequest(
- undefined, "GET", path, undefined,
- );
+ try {
+ const result = await this._http.authedRequest(
+ undefined, "GET", path, undefined,
+ );
+ return result;
+ } catch (e) {
+ if (e.data && e.data.errcode === 'M_NOT_FOUND') {
+ return null;
+ }
+ throw e;
+ }
};
/**
@@ -2439,6 +2481,7 @@ MatrixClient.prototype._sendCompleteEvent = function(roomId, eventObject, txnId,
const localEvent = new MatrixEvent(Object.assign(eventObject, {
event_id: "~" + roomId + ":" + txnId,
user_id: this.credentials.userId,
+ sender: this.credentials.userId,
room_id: roomId,
origin_server_ts: new Date().getTime(),
}));
@@ -2949,25 +2992,32 @@ MatrixClient.prototype.setRoomReadMarkers = async function(
* May return synthesized attributes if the URL lacked OG meta.
*/
MatrixClient.prototype.getUrlPreview = function(url, ts, callback) {
+ // bucket the timestamp to the nearest minute to prevent excessive spam to the server
+ // Surely 60-second accuracy is enough for anyone.
+ ts = Math.floor(ts / 60000) * 60000;
+
const key = ts + "_" + url;
- const og = this.urlPreviewCache[key];
- if (og) {
- return Promise.resolve(og);
+
+ // If there's already a request in flight (or we've handled it), return that instead.
+ const cachedPreview = this.urlPreviewCache[key];
+ if (cachedPreview) {
+ if (callback) {
+ cachedPreview.then(callback).catch(callback);
+ }
+ return cachedPreview;
}
- const self = this;
- return this._http.authedRequest(
+ const resp = this._http.authedRequest(
callback, "GET", "/preview_url", {
url: url,
ts: ts,
}, undefined, {
prefix: PREFIX_MEDIA_R0,
},
- ).then(function(response) {
- // TODO: expire cache occasionally
- self.urlPreviewCache[key] = response;
- return response;
- });
+ );
+ // TODO: Expire the URL preview cache sometimes
+ this.urlPreviewCache[key] = resp;
+ return resp;
};
/**
@@ -3469,46 +3519,6 @@ MatrixClient.prototype.setPresence = function(opts, callback) {
);
};
-function _presenceList(callback, client, opts, method) {
- const path = utils.encodeUri("/presence/list/$userId", {
- $userId: client.credentials.userId,
- });
- return client._http.authedRequest(callback, method, path, undefined, opts);
-}
-
-/**
-* Retrieve current user presence list.
-* @param {module:client.callback} callback Optional.
-* @return {Promise} Resolves: TODO
-* @return {module:http-api.MatrixError} Rejects: with an error response.
-*/
-MatrixClient.prototype.getPresenceList = function(callback) {
- return _presenceList(callback, this, undefined, "GET");
-};
-
-/**
-* Add users to the current user presence list.
-* @param {module:client.callback} callback Optional.
-* @param {string[]} userIds
-* @return {Promise} Resolves: TODO
-* @return {module:http-api.MatrixError} Rejects: with an error response.
-*/
-MatrixClient.prototype.inviteToPresenceList = function(callback, userIds) {
- const opts = {"invite": userIds};
- return _presenceList(callback, this, opts, "POST");
-};
-
-/**
-* Drop users from the current user presence list.
-* @param {module:client.callback} callback Optional.
-* @param {string[]} userIds
-* @return {Promise} Resolves: TODO
-* @return {module:http-api.MatrixError} Rejects: with an error response.
-**/
-MatrixClient.prototype.dropFromPresenceList = function(callback, userIds) {
- const opts = {"drop": userIds};
- return _presenceList(callback, this, opts, "POST");
-};
/**
* Retrieve older messages from the given room and put them in the timeline.
@@ -4499,64 +4509,54 @@ MatrixClient.prototype.getFilter = function(userId, filterId, allowCached) {
* @param {Filter} filter
* @return {Promise<String>} Filter ID
*/
-MatrixClient.prototype.getOrCreateFilter = function(filterName, filter) {
+MatrixClient.prototype.getOrCreateFilter = async function(filterName, filter) {
const filterId = this.store.getFilterIdByName(filterName);
- let promise = Promise.resolve();
- const self = this;
+ let existingId = undefined;
if (filterId) {
// check that the existing filter matches our expectations
- promise = self.getFilter(self.credentials.userId,
- filterId, true,
- ).then(function(existingFilter) {
- const oldDef = existingFilter.getDefinition();
- const newDef = filter.getDefinition();
-
- if (utils.deepCompare(oldDef, newDef)) {
- // super, just use that.
- // debuglog("Using existing filter ID %s: %s", filterId,
- // JSON.stringify(oldDef));
- return Promise.resolve(filterId);
+ try {
+ const existingFilter =
+ await this.getFilter(this.credentials.userId, filterId, true);
+ if (existingFilter) {
+ const oldDef = existingFilter.getDefinition();
+ const newDef = filter.getDefinition();
+
+ if (utils.deepCompare(oldDef, newDef)) {
+ // super, just use that.
+ // debuglog("Using existing filter ID %s: %s", filterId,
+ // JSON.stringify(oldDef));
+ existingId = filterId;
+ }
}
- // debuglog("Existing filter ID %s: %s; new filter: %s",
- // filterId, JSON.stringify(oldDef), JSON.stringify(newDef));
- self.store.setFilterIdByName(filterName, undefined);
- return undefined;
- }, function(error) {
+ } catch (error) {
// Synapse currently returns the following when the filter cannot be found:
// {
// errcode: "M_UNKNOWN",
// name: "M_UNKNOWN",
// message: "No row found",
- // data: Object, httpStatus: 404
// }
- if (error.httpStatus === 404 &&
- (error.errcode === "M_UNKNOWN" || error.errcode === "M_NOT_FOUND")) {
- // Clear existing filterId from localStorage
- // if it no longer exists on the server
- self.store.setFilterIdByName(filterName, undefined);
- // Return a undefined value for existingId further down the promise chain
- return undefined;
- } else {
+ if (error.errcode !== "M_UNKNOWN" && error.errcode !== "M_NOT_FOUND") {
throw error;
}
- });
+ }
+ // if the filter doesn't exist anymore on the server, remove from store
+ if (!existingId) {
+ this.store.setFilterIdByName(filterName, undefined);
+ }
}
- return promise.then(function(existingId) {
- if (existingId) {
- return existingId;
- }
+ if (existingId) {
+ return existingId;
+ }
- // create a new filter
- return self.createFilter(filter.getDefinition(),
- ).then(function(createdFilter) {
- // debuglog("Created new filter ID %s: %s", createdFilter.filterId,
- // JSON.stringify(createdFilter.getDefinition()));
- self.store.setFilterIdByName(filterName, createdFilter.filterId);
- return createdFilter.filterId;
- });
- });
+ // create a new filter
+ const createdFilter = await this.createFilter(filter.getDefinition());
+
+ // debuglog("Created new filter ID %s: %s", createdFilter.filterId,
+ // JSON.stringify(createdFilter.getDefinition()));
+ this.store.setFilterIdByName(filterName, createdFilter.filterId);
+ return createdFilter.filterId;
};
@@ -4720,6 +4720,9 @@ MatrixClient.prototype.deactivateSynapseUser = function(userId) {
* @param {Boolean=} opts.lazyLoadMembers True to not load all membership events during
* initial sync but fetch them when needed by calling `loadOutOfBandMembers`
* This will override the filter option at this moment.
+ * @param {Number=} opts.clientWellKnownPollPeriod The number of seconds between polls
+ * to /.well-known/matrix/client, undefined to disable. This should be in the order of hours.
+ * Default: undefined.
*/
MatrixClient.prototype.startClient = async function(opts) {
if (this.clientRunning) {
@@ -4734,6 +4737,13 @@ MatrixClient.prototype.startClient = async function(opts) {
};
}
+ // Create our own user object artificially (instead of waiting for sync)
+ // so it's always available, even if the user is not in any rooms etc.
+ const userId = this.getUserId();
+ if (userId) {
+ this.store.storeUser(new User(userId));
+ }
+
if (this._crypto) {
this._crypto.uploadDeviceKeys();
this._crypto.start();
@@ -4761,6 +4771,29 @@ MatrixClient.prototype.startClient = async function(opts) {
this._clientOpts = opts;
this._syncApi = new SyncApi(this, opts);
this._syncApi.sync();
+
+ if (opts.clientWellKnownPollPeriod !== undefined) {
+ this._clientWellKnownIntervalID =
+ setInterval(() => {
+ this._fetchClientWellKnown();
+ }, 1000 * opts.clientWellKnownPollPeriod);
+ this._fetchClientWellKnown();
+ }
+};
+
+MatrixClient.prototype._fetchClientWellKnown = async function() {
+ try {
+ this._clientWellKnown = await AutoDiscovery.getRawClientConfig(this.getDomain());
+ this.emit("WellKnown.client", this._clientWellKnown);
+ } catch (err) {
+ logger.error("Failed to get client well-known", err);
+ this._clientWellKnown = undefined;
+ this.emit("WellKnown.error", err);
+ }
+};
+
+MatrixClient.prototype.getClientWellKnown = function() {
+ return this._clientWellKnown;
};
/**
@@ -4802,6 +4835,9 @@ MatrixClient.prototype.stopClient = function() {
this._peekSync.stopPeeking();
}
global.clearTimeout(this._checkTurnServersTimeoutID);
+ if (this._clientWellKnownIntervalID !== undefined) {
+ global.clearInterval(this._clientWellKnownIntervalID);
+ }
};
/**
@@ -5247,17 +5283,20 @@ function _resolve(callback, resolve, res) {
resolve(res);
}
-function _PojoToMatrixEventMapper(client) {
+function _PojoToMatrixEventMapper(client, options) {
+ const preventReEmit = Boolean(options && options.preventReEmit);
function mapper(plainOldJsObject) {
const event = new MatrixEvent(plainOldJsObject);
if (event.isEncrypted()) {
- client.reEmitter.reEmit(event, [
- "Event.decrypted",
- ]);
+ if (!preventReEmit) {
+ client.reEmitter.reEmit(event, [
+ "Event.decrypted",
+ ]);
+ }
event.attemptDecryption(client._crypto);
}
const room = client.getRoom(event.getRoomId());
- if (room) {
+ if (room && !preventReEmit) {
room.reEmitter.reEmit(event, ["Event.replaced"]);
}
return event;
@@ -5266,10 +5305,12 @@ function _PojoToMatrixEventMapper(client) {
}
/**
+ * @param {object} [options]
+ * @param {bool} options.preventReEmit don't reemit events emitted on an event mapped by this mapper on the client
* @return {Function}
*/
-MatrixClient.prototype.getEventMapper = function() {
- return _PojoToMatrixEventMapper(this);
+MatrixClient.prototype.getEventMapper = function(options = undefined) {
+ return _PojoToMatrixEventMapper(this, options);
};
/**
@@ -5556,6 +5597,22 @@ MatrixClient.prototype.generateClientSecret = function() {
*/
/**
+ * Fires whenever the stored devices for a user have changed
+ * @event module:client~MatrixClient#"crypto.devicesUpdated"
+ * @param {String[]} users A list of user IDs that were updated
+ * @param {bool} initialFetch If true, the store was empty (apart
+ * from our own device) and has been seeded.
+ */
+
+/**
+ * Fires whenever the stored devices for a user will be updated
+ * @event module:client~MatrixClient#"crypto.willUpdateDevices"
+ * @param {String[]} users A list of user IDs that will be updated
+ * @param {bool} initialFetch If true, the store is empty (apart
+ * from our own device) and is being seeded.
+ */
+
+/**
* Fires whenever the status of e2e key backup changes, as returned by getKeyBackupEnabled()
* @event module:client~MatrixClient#"crypto.keyBackupStatus"
* @param {bool} enabled true if key backup has been enabled, otherwise false
diff --git a/src/crypto/CrossSigning.js b/src/crypto/CrossSigning.js
index c0633e95..ff5bc708 100644
--- a/src/crypto/CrossSigning.js
+++ b/src/crypto/CrossSigning.js
@@ -24,6 +24,7 @@ import {decodeBase64, encodeBase64, pkSign, pkVerify} from './olmlib';
import {EventEmitter} from 'events';
import {logger} from '../logger';
import {IndexedDBCryptoStore} from '../crypto/store/indexeddb-crypto-store';
+import {decryptAES, encryptAES} from './aes';
function publicKeyFromKeyInfo(keyInfo) {
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
@@ -310,6 +311,13 @@ export class CrossSigningInfo extends EventEmitter {
}
}
+ /**
+ * unsets the keys, used when another session has reset the keys, to disable cross-signing
+ */
+ clearKeys() {
+ this.keys = {};
+ }
+
setKeys(keys) {
const signingKeys = {};
if (keys.master) {
@@ -630,10 +638,10 @@ export class DeviceTrustLevel {
}
}
-export function createCryptoStoreCacheCallbacks(store) {
+export function createCryptoStoreCacheCallbacks(store, olmdevice) {
return {
- getCrossSigningKeyCache: function(type, _expectedPublicKey) {
- return new Promise((resolve) => {
+ getCrossSigningKeyCache: async function(type, _expectedPublicKey) {
+ const key = await new Promise((resolve) => {
return store.doTxn(
'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT],
@@ -642,13 +650,23 @@ export function createCryptoStoreCacheCallbacks(store) {
},
);
});
+
+ if (key && key.ciphertext) {
+ const pickleKey = Buffer.from(olmdevice._pickleKey);
+ const decrypted = await decryptAES(key, pickleKey, type);
+ return decodeBase64(decrypted);
+ } else {
+ return key;
+ }
},
- storeCrossSigningKeyCache: function(type, key) {
+ storeCrossSigningKeyCache: async function(type, key) {
if (!(key instanceof Uint8Array)) {
throw new Error(
`storeCrossSigningKeyCache expects Uint8Array, got ${key}`,
);
}
+ const pickleKey = Buffer.from(olmdevice._pickleKey);
+ key = await encryptAES(encodeBase64(key), pickleKey, type);
return store.doTxn(
'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT],
diff --git a/src/crypto/DeviceList.js b/src/crypto/DeviceList.js
index 3cf40f69..5925f8ad 100644
--- a/src/crypto/DeviceList.js
+++ b/src/crypto/DeviceList.js
@@ -109,6 +109,9 @@ export class DeviceList extends EventEmitter {
this._savePromiseTime = null;
// The timer used to delay the save
this._saveTimer = null;
+ // True if we have fetched data from the server or loaded a non-empty
+ // set of device data from the store
+ this._hasFetched = null;
}
/**
@@ -118,6 +121,7 @@ export class DeviceList extends EventEmitter {
await this._cryptoStore.doTxn(
'readonly', [IndexedDBCryptoStore.STORE_DEVICE_DATA], (txn) => {
this._cryptoStore.getEndToEndDeviceData(txn, (deviceData) => {
+ this._hasFetched = Boolean(deviceData && deviceData.devices);
this._devices = deviceData ? deviceData.devices : {},
this._crossSigningInfo = deviceData ?
deviceData.crossSigningInfo || {} : {};
@@ -652,6 +656,7 @@ export class DeviceList extends EventEmitter {
});
const finished = (success) => {
+ this.emit("crypto.willUpdateDevices", users, !this._hasFetched);
users.forEach((u) => {
this._dirty = true;
@@ -677,7 +682,8 @@ export class DeviceList extends EventEmitter {
}
});
this.saveIfDirty();
- this.emit("crypto.devicesUpdated", users);
+ this.emit("crypto.devicesUpdated", users, !this._hasFetched);
+ this._hasFetched = true;
};
return prom;
diff --git a/src/crypto/OlmDevice.js b/src/crypto/OlmDevice.js
index 5b6f1183..6300609a 100644
--- a/src/crypto/OlmDevice.js
+++ b/src/crypto/OlmDevice.js
@@ -36,9 +36,15 @@ function checkPayloadLength(payloadString) {
// Note that even if we manage to do the encryption, the message send may fail,
// because by the time we've wrapped the ciphertext in the event object, it may
// exceed 65K. But at least we won't just fail with "abort()" in that case.
- throw new Error("Message too long (" + payloadString.length + " bytes). " +
+ const err = new Error("Message too long (" + payloadString.length + " bytes). " +
"The maximum for an encrypted message is " +
MAX_PLAINTEXT_LENGTH + " bytes.");
+ // TODO: [TypeScript] We should have our own error types
+ err.data = {
+ errcode: "M_TOO_LARGE",
+ error: "Payload too large for encrypted message",
+ };
+ throw err;
}
}
@@ -105,6 +111,9 @@ export function OlmDevice(cryptoStore) {
// Keep track of sessions that we're starting, so that we don't start
// multiple sessions for the same device at the same time.
this._sessionsInProgress = {};
+
+ // Used by olm to serialise prekey message decryptions
+ this._olmPrekeyPromise = Promise.resolve();
}
/**
@@ -1029,6 +1038,11 @@ OlmDevice.prototype.addInboundGroupSession = async function(
}
}
+ logger.info(
+ "Storing megolm session " + senderKey + "/" + sessionId +
+ " with first index " + session.first_known_index(),
+ );
+
const sessionData = {
room_id: roomId,
session: session.pickle(this._pickleKey),
diff --git a/src/crypto/OutgoingRoomKeyRequestManager.js b/src/crypto/OutgoingRoomKeyRequestManager.js
index 15dd09c2..c3f314f7 100644
--- a/src/crypto/OutgoingRoomKeyRequestManager.js
+++ b/src/crypto/OutgoingRoomKeyRequestManager.js
@@ -97,10 +97,6 @@ export class OutgoingRoomKeyRequestManager {
*/
start() {
this._clientRunning = true;
-
- // set the timer going, to handle any requests which didn't get sent
- // on the previous run of the client.
- this._startTimer();
}
/**
@@ -113,7 +109,14 @@ export class OutgoingRoomKeyRequestManager {
}
/**
- * Send off a room key request, if we haven't already done so.
+ * Send any requests that have been queued
+ */
+ sendQueuedRequests() {
+ this._startTimer();
+ }
+
+ /**
+ * Queue up a room key request, if we haven't already queued or sent one.
*
* The `requestBody` is compared (with a deep-equality check) against
* previous queued or sent requests and if it matches, no change is made.
@@ -129,7 +132,7 @@ export class OutgoingRoomKeyRequestManager {
* pending list (or we have established that a similar request already
* exists)
*/
- async sendRoomKeyRequest(requestBody, recipients, resend=false) {
+ async queueRoomKeyRequest(requestBody, recipients, resend=false) {
const req = await this._cryptoStore.getOutgoingRoomKeyRequest(
requestBody,
);
@@ -184,7 +187,7 @@ export class OutgoingRoomKeyRequestManager {
// in state ROOM_KEY_REQUEST_STATES.SENT, so we must have
// raced with another tab to mark the request cancelled.
// Try again, to make sure the request is resent.
- return await this.sendRoomKeyRequest(
+ return await this.queueRoomKeyRequest(
requestBody, recipients, resend,
);
}
@@ -220,9 +223,6 @@ export class OutgoingRoomKeyRequestManager {
throw new Error('unhandled state: ' + req.state);
}
}
- // some of the branches require the timer to be started. Just start it
- // all the time, because it doesn't hurt to start it.
- this._startTimer();
}
/**
@@ -332,14 +332,14 @@ export class OutgoingRoomKeyRequestManager {
* This is intended for situations where something substantial has changed, and we
* don't really expect the other end to even care about the cancellation.
* For example, after initialization or self-verification.
- * @return {Promise} An array of `sendRoomKeyRequest` outputs.
+ * @return {Promise} An array of `queueRoomKeyRequest` outputs.
*/
async cancelAndResendAllOutgoingRequests() {
const outgoings = await this._cryptoStore.getAllOutgoingRoomKeyRequestsByState(
ROOM_KEY_REQUEST_STATES.SENT,
);
return Promise.all(outgoings.map(({ requestBody, recipients }) =>
- this.sendRoomKeyRequest(requestBody, recipients, true)));
+ this.queueRoomKeyRequest(requestBody, recipients, true)));
}
// start the background timer to send queued requests, if the timer isn't
@@ -381,15 +381,12 @@ export class OutgoingRoomKeyRequestManager {
return Promise.resolve();
}
- logger.log("Looking for queued outgoing room key requests");
-
return this._cryptoStore.getOutgoingRoomKeyRequestByState([
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING,
ROOM_KEY_REQUEST_STATES.CANCELLATION_PENDING_AND_WILL_RESEND,
ROOM_KEY_REQUEST_STATES.UNSENT,
]).then((req) => {
if (!req) {
- logger.log("No more outgoing room key requests");
this._sendOutgoingRoomKeyRequestsTimer = null;
return;
}
@@ -413,7 +410,6 @@ export class OutgoingRoomKeyRequestManager {
}).catch((e) => {
logger.error("Error sending room key request; will retry later.", e);
this._sendOutgoingRoomKeyRequestsTimer = null;
- this._startTimer();
});
});
}
diff --git a/src/crypto/SecretStorage.js b/src/crypto/SecretStorage.js
index fba19f47..ecfa96db 100644
--- a/src/crypto/SecretStorage.js
+++ b/src/crypto/SecretStorage.js
@@ -17,15 +17,12 @@ limitations under the License.
import {EventEmitter} from 'events';
import {logger} from '../logger';
import * as olmlib from './olmlib';
-import {pkVerify} from './olmlib';
import {randomString} from '../randomstring';
import {encryptAES, decryptAES} from './aes';
+import {encodeBase64} from "./olmlib";
export const SECRET_STORAGE_ALGORITHM_V1_AES
= "m.secret_storage.v1.aes-hmac-sha2";
-// don't use curve25519 for writing data.
-export const SECRET_STORAGE_ALGORITHM_V1_CURVE25519
- = "m.secret_storage.v1.curve25519-aes-sha2";
const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
@@ -34,11 +31,10 @@ const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
* @module crypto/SecretStorage
*/
export class SecretStorage extends EventEmitter {
- constructor(baseApis, cryptoCallbacks, crossSigningInfo) {
+ constructor(baseApis, cryptoCallbacks) {
super();
this._baseApis = baseApis;
this._cryptoCallbacks = cryptoCallbacks;
- this._crossSigningInfo = crossSigningInfo;
this._requests = {};
this._incomingRequests = {};
}
@@ -52,7 +48,7 @@ export class SecretStorage extends EventEmitter {
}
setDefaultKeyId(keyId) {
- return new Promise((resolve) => {
+ return new Promise(async (resolve, reject) => {
const listener = (ev) => {
if (
ev.getType() === 'm.secret_storage.default_key' &&
@@ -64,10 +60,15 @@ export class SecretStorage extends EventEmitter {
};
this._baseApis.on('accountData', listener);
- this._baseApis.setAccountData(
- 'm.secret_storage.default_key',
- { key: keyId },
- );
+ try {
+ await this._baseApis.setAccountData(
+ 'm.secret_storage.default_key',
+ { key: keyId },
+ );
+ } catch (e) {
+ this._baseApis.removeListener('accountData', listener);
+ reject(e);
+ }
});
}
@@ -91,20 +92,16 @@ export class SecretStorage extends EventEmitter {
keyData.name = opts.name;
}
- switch (algorithm) {
- case SECRET_STORAGE_ALGORITHM_V1_AES:
- {
+ if (algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (opts.passphrase) {
keyData.passphrase = opts.passphrase;
}
if (opts.key) {
- const {iv, mac} = await encryptAES(ZERO_STR, opts.key, "");
+ const {iv, mac} = await SecretStorage._calculateKeyCheck(opts.key);
keyData.iv = iv;
keyData.mac = mac;
}
- break;
- }
- default:
+ } else {
throw new Error(`Unknown key algorithm ${opts.algorithm}`);
}
@@ -118,8 +115,6 @@ export class SecretStorage extends EventEmitter {
);
}
- await this._crossSigningInfo.signObject(keyData, 'master');
-
await this._baseApis.setAccountData(
`m.secret_storage.key.${keyId}`, keyData,
);
@@ -128,33 +123,6 @@ export class SecretStorage extends EventEmitter {
}
/**
- * Signs a given secret storage key with the cross-signing master key.
- *
- * @param {string} [keyId = default key's ID] The ID of the key to sign.
- * Defaults to the default key ID if not provided.
- */
- async signKey(keyId) {
- if (!keyId) {
- keyId = await this.getDefaultKeyId();
- }
- if (!keyId) {
- throw new Error("signKey requires a key ID");
- }
-
- const keyInfo = await this._baseApis.getAccountDataFromServer(
- `m.secret_storage.key.${keyId}`,
- );
- if (!keyInfo) {
- throw new Error(`Key ${keyId} does not exist in account data`);
- }
-
- await this._crossSigningInfo.signObject(keyInfo, 'master');
- await this._baseApis.setAccountData(
- `m.secret_storage.key.${keyId}`, keyInfo,
- );
- }
-
- /**
* Get the key information for a given ID.
*
* @param {string} [keyId = default key's ID] The ID of the key to check
@@ -187,15 +155,6 @@ export class SecretStorage extends EventEmitter {
return !!(await this.getKey(keyId));
}
- async keyNeedsUpgrade(keyId) {
- const keyInfo = await this.getKey(keyId);
- if (keyInfo && keyInfo[1].algorithm === SECRET_STORAGE_ALGORITHM_V1_CURVE25519) {
- return true;
- } else {
- return false;
- }
- }
-
/**
* Check whether a key matches what we expect based on the key info
*
@@ -205,36 +164,23 @@ export class SecretStorage extends EventEmitter {
* @return {boolean} whether or not the key matches
*/
async checkKey(key, info) {
- switch (info.algorithm) {
- case SECRET_STORAGE_ALGORITHM_V1_AES:
- {
+ if (info.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (info.mac) {
- const {mac} = await encryptAES(ZERO_STR, key, "", info.iv);
- return info.mac === mac;
+ const {mac} = await SecretStorage._calculateKeyCheck(key, info.iv);
+ return info.mac.replace(/=+$/g, '') === mac.replace(/=+$/g, '');
} else {
// if we have no information, we have to assume the key is right
return true;
}
- }
- case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
- {
- let decryption = null;
- try {
- decryption = new global.Olm.PkDecryption();
- const gotPubkey = decryption.init_with_private_key(key);
- // make sure it agrees with the given pubkey
- return gotPubkey === info.pubkey;
- } catch (e) {
- return false;
- } finally {
- if (decryption) decryption.free();
- }
- }
- default:
+ } else {
throw new Error("Unknown algorithm");
}
}
+ static async _calculateKeyCheck(key, iv) {
+ return await encryptAES(ZERO_STR, key, "", iv);
+ }
+
/**
* Store an encrypted secret on the server
*
@@ -268,15 +214,11 @@ export class SecretStorage extends EventEmitter {
}
// encrypt secret, based on the algorithm
- switch (keyInfo.algorithm) {
- case SECRET_STORAGE_ALGORITHM_V1_AES:
- {
+ if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
const keys = {[keyId]: keyInfo};
const [, encryption] = await this._getSecretStorageKey(keys, name);
encrypted[keyId] = await encryption.encrypt(secret);
- break;
- }
- default:
+ } else {
logger.warn("unknown algorithm for secret storage key " + keyId
+ ": " + keyInfo.algorithm);
// do nothing if we don't understand the encryption algorithm
@@ -342,24 +284,11 @@ export class SecretStorage extends EventEmitter {
"m.secret_storage.key." + keyId,
);
const encInfo = secretInfo.encrypted[keyId];
- switch (keyInfo.algorithm) {
- case SECRET_STORAGE_ALGORITHM_V1_AES:
+ // only use keys we understand the encryption algorithm of
+ if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (encInfo.iv && encInfo.ciphertext && encInfo.mac) {
keys[keyId] = keyInfo;
}
- break;
- case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
- if (
- keyInfo.pubkey && (
- (encInfo.ciphertext && encInfo.mac && encInfo.ephemeral) ||
- encInfo.passthrough
- )
- ) {
- keys[keyId] = keyInfo;
- }
- break;
- default:
- // do nothing if we don't understand the encryption algorithm
}
}
@@ -377,8 +306,9 @@ export class SecretStorage extends EventEmitter {
const encInfo = secretInfo.encrypted[keyId];
// We don't actually need the decryption object if it's a passthrough
- // since we just want to return the key itself.
- if (encInfo.passthrough) return decryption.get_private_key();
+ // since we just want to return the key itself. It must be base64
+ // encoded, since this is how a key would normally be stored.
+ if (encInfo.passthrough) return encodeBase64(decryption.get_private_key());
return await decryption.decrypt(encInfo);
} finally {
@@ -412,8 +342,7 @@ export class SecretStorage extends EventEmitter {
const ret = {};
- // check if secret is encrypted by a known/trusted secret and
- // encryption looks sane
+ // filter secret encryption keys with supported algorithm
for (const keyId of Object.keys(secretInfo.encrypted)) {
// get key information from key storage
const keyInfo = await this._baseApis.getAccountDataFromServer(
@@ -422,49 +351,11 @@ export class SecretStorage extends EventEmitter {
if (!keyInfo) continue;
const encInfo = secretInfo.encrypted[keyId];
- // We don't actually need the decryption object if it's a passthrough
- // since we just want to return the key itself.
- if (encInfo.passthrough) {
- try {
- pkVerify(
- keyInfo,
- this._crossSigningInfo.getId('master'),
- this._crossSigningInfo.userId,
- );
- } catch (e) {
- // not trusted, so move on to the next key
- continue;
- }
- ret[keyId] = keyInfo;
- continue;
- }
-
- switch (keyInfo.algorithm) {
- case SECRET_STORAGE_ALGORITHM_V1_AES:
+ // only use keys we understand the encryption algorithm of
+ if (keyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
if (encInfo.iv && encInfo.ciphertext && encInfo.mac) {
ret[keyId] = keyInfo;
}
- break;
- case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
- if (keyInfo.pubkey && encInfo.ciphertext && encInfo.mac
- && encInfo.ephemeral) {
- if (checkKey) {
- try {
- pkVerify(
- keyInfo,
- this._crossSigningInfo.getId('master'),
- this._crossSigningInfo.userId,
- );
- } catch (e) {
- // not trusted, so move on to the next key
- continue;
- }
- }
- ret[keyId] = keyInfo;
- }
- break;
- default:
- // do nothing if we don't understand the encryption algorithm
}
}
return Object.keys(ret).length ? ret : null;
@@ -591,7 +482,7 @@ export class SecretStorage extends EventEmitter {
this._baseApis,
{
[sender]: [
- await this._baseApis.getStoredDevice(sender, deviceId),
+ this._baseApis.getStoredDevice(sender, deviceId),
],
},
);
@@ -601,7 +492,7 @@ export class SecretStorage extends EventEmitter {
this._baseApis.deviceId,
this._baseApis._crypto._olmDevice,
sender,
- this._baseApis._crypto.getStoredDevice(sender, deviceId),
+ this._baseApis.getStoredDevice(sender, deviceId),
payload,
);
const contentMap = {
@@ -668,9 +559,7 @@ export class SecretStorage extends EventEmitter {
throw new Error("App returned unknown key from getSecretStorageKey!");
}
- switch (keys[keyId].algorithm) {
- case SECRET_STORAGE_ALGORITHM_V1_AES:
- {
+ if (keys[keyId].algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
const decryption = {
encrypt: async function(secret) {
return await encryptAES(secret, privateKey, name);
@@ -680,36 +569,7 @@ export class SecretStorage extends EventEmitter {
},
};
return [keyId, decryption];
- }
- case SECRET_STORAGE_ALGORITHM_V1_CURVE25519:
- {
- const pkDecryption = new global.Olm.PkDecryption();
- let pubkey;
- try {
- pubkey = pkDecryption.init_with_private_key(privateKey);
- } catch (e) {
- pkDecryption.free();
- throw new Error("getSecretStorageKey callback returned invalid key");
- }
- if (pubkey !== keys[keyId].pubkey) {
- pkDecryption.free();
- throw new Error(
- "getSecretStorageKey callback returned incorrect key",
- );
- }
- const decryption = {
- free: pkDecryption.free.bind(pkDecryption),
- decrypt: async function(encInfo) {
- return pkDecryption.decrypt(
- encInfo.ephemeral, encInfo.mac, encInfo.ciphertext,
- );
- },
- // needed for passthrough
- get_private_key: pkDecryption.get_private_key.bind(pkDecryption),
- };
- return [keyId, decryption];
- }
- default:
+ } else {
throw new Error("Unknown key type: " + keys[keyId].algorithm);
}
}
diff --git a/src/crypto/aes.js b/src/crypto/aes.js
index 6c351330..1556413f 100644
--- a/src/crypto/aes.js
+++ b/src/crypto/aes.js
@@ -84,9 +84,9 @@ async function decryptNode(data, key, name) {
const [aesKey, hmacKey] = deriveKeysNode(key, name);
const hmac = crypto.createHmac("sha256", hmacKey)
- .update(data.ciphertext, "base64").digest("base64");
+ .update(data.ciphertext, "base64").digest("base64").replace(/=+$/g, '');
- if (hmac !== data.mac) {
+ if (hmac !== data.mac.replace(/=+$/g, '')) {
throw new Error(`Error decrypting secret ${name}: bad MAC`);
}
diff --git a/src/crypto/algorithms/megolm.js b/src/crypto/algorithms/megolm.js
index 5d9a9bf7..0976302a 100644
--- a/src/crypto/algorithms/megolm.js
+++ b/src/crypto/algorithms/megolm.js
@@ -311,7 +311,7 @@ MegolmEncryption.prototype._ensureOutboundSession = async function(
}
await this._shareKeyWithDevices(
- session, key, payload, retryDevices, failedDevices,
+ session, key, payload, retryDevices, failedDevices, 30000,
);
await this._notifyFailedOlmDevices(session, key, failedDevices);
@@ -521,6 +521,33 @@ MegolmEncryption.prototype._encryptAndSendKeysToDevices = function(
}
return Promise.all(promises).then(() => {
+ // prune out any devices that encryptMessageForDevice could not encrypt for,
+ // in which case it will have just not added anything to the ciphertext object.
+ // There's no point sending messages to devices if we couldn't encrypt to them,
+ // since that's effectively a blank message.
+ for (const userId of Object.keys(contentMap)) {
+ for (const deviceId of Object.keys(contentMap[userId])) {
+ if (Object.keys(contentMap[userId][deviceId].ciphertext).length === 0) {
+ logger.log(
+ "No ciphertext for device " +
+ userId + ":" + deviceId + ": pruning",
+ );
+ delete contentMap[userId][deviceId];
+ }
+ }
+ // No devices left for that user? Strip that too.
+ if (Object.keys(contentMap[userId]).length === 0) {
+ logger.log("Pruned all devices for user " + userId);
+ delete contentMap[userId];
+ }
+ }
+
+ // Is there anything left?
+ if (Object.keys(contentMap).length === 0) {
+ logger.log("No users left to send to: aborting");
+ return;
+ }
+
return this._baseApis.sendToDevice("m.room.encrypted", contentMap).then(() => {
// store that we successfully uploaded the keys of the current slice
for (const userId of Object.keys(contentMap)) {
@@ -1296,7 +1323,6 @@ MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
keysClaimed = event.getKeysClaimed();
}
- logger.log(`Received and adding key for megolm session ${senderKey}|${sessionId}`);
return this._olmDevice.addInboundGroupSession(
content.room_id, senderKey, forwardingKeyChain, sessionId,
content.session_key, keysClaimed,
@@ -1555,7 +1581,8 @@ MegolmDecryption.prototype.importRoomKey = function(session) {
};
/**
- * Have another go at decrypting events after we receive a key
+ * Have another go at decrypting events after we receive a key. Resolves once
+ * decryption has been re-attempted on all events.
*
* @private
* @param {String} senderKey
@@ -1574,21 +1601,17 @@ MegolmDecryption.prototype._retryDecryption = async function(senderKey, sessionI
return true;
}
- pending.delete(sessionId);
- if (pending.size === 0) {
- this._pendingEvents[senderKey];
- }
+ logger.debug("Retrying decryption on events", [...pending]);
await Promise.all([...pending].map(async (ev) => {
try {
- await ev.attemptDecryption(this._crypto);
+ await ev.attemptDecryption(this._crypto, true);
} catch (e) {
// don't die if something goes wrong
}
}));
- // ev.attemptDecryption will re-add to this._pendingEvents if an event
- // couldn't be decrypted
+ // If decrypted successfully, they'll have been removed from _pendingEvents
return !((this._pendingEvents[senderKey] || {})[sessionId]);
};
diff --git a/src/crypto/algorithms/olm.js b/src/crypto/algorithms/olm.js
index 5c30a178..6add378e 100644
--- a/src/crypto/algorithms/olm.js
+++ b/src/crypto/algorithms/olm.js
@@ -265,6 +265,25 @@ OlmDecryption.prototype.decryptEvent = async function(event) {
OlmDecryption.prototype._decryptMessage = async function(
theirDeviceIdentityKey, message,
) {
+ // This is a wrapper that serialises decryptions of prekey messages, because
+ // otherwise we race between deciding we have no active sessions for the message
+ // and creating a new one, which we can only do once because it removes the OTK.
+ if (message.type !== 0) {
+ // not a prekey message: we can safely just try & decrypt it
+ return this._reallyDecryptMessage(theirDeviceIdentityKey, message);
+ } else {
+ const myPromise = this._olmDevice._olmPrekeyPromise.then(() => {
+ return this._reallyDecryptMessage(theirDeviceIdentityKey, message);
+ });
+ // we want the error, but don't propagate it to the next decryption
+ this._olmDevice._olmPrekeyPromise = myPromise.catch(() => {});
+ return await myPromise;
+ }
+};
+
+OlmDecryption.prototype._reallyDecryptMessage = async function(
+ theirDeviceIdentityKey, message,
+) {
const sessionIds = await this._olmDevice.getSessionIdsForDevice(
theirDeviceIdentityKey,
);
diff --git a/src/crypto/index.js b/src/crypto/index.js
index b8b02fba..06cc2add 100644
--- a/src/crypto/index.js
+++ b/src/crypto/index.js
@@ -56,6 +56,7 @@ import {ToDeviceChannel, ToDeviceRequests} from "./verification/request/ToDevice
import * as httpApi from "../http-api";
import {IllegalMethod} from "./verification/IllegalMethod";
import {KeySignatureUploadError} from "../errors";
+import {decryptAES, encryptAES} from './aes';
const DeviceVerification = DeviceInfo.DeviceVerification;
@@ -169,7 +170,9 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
this._deviceList.on(
'userCrossSigningUpdated', this._onDeviceListUserCrossSigningUpdated,
);
- this._reEmitter.reEmit(this._deviceList, ["crypto.devicesUpdated"]);
+ this._reEmitter.reEmit(this._deviceList, [
+ "crypto.devicesUpdated", "crypto.willUpdateDevices",
+ ]);
// the last time we did a check for the number of one-time-keys on the
// server.
@@ -223,8 +226,13 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
this._toDeviceVerificationRequests = new ToDeviceRequests();
this._inRoomVerificationRequests = new InRoomRequests();
+ // This flag will be unset whilst the client processes a sync response
+ // so that we don't start requesting keys until we've actually finished
+ // processing the response.
+ this._sendKeyRequestsImmediately = false;
+
const cryptoCallbacks = this._baseApis._cryptoCallbacks || {};
- const cacheCallbacks = createCryptoStoreCacheCallbacks(cryptoStore);
+ const cacheCallbacks = createCryptoStoreCacheCallbacks(cryptoStore, this._olmDevice);
this._crossSigningInfo = new CrossSigningInfo(
userId,
@@ -233,7 +241,7 @@ export function Crypto(baseApis, sessionStore, userId, deviceId,
);
this._secretStorage = new SecretStorage(
- baseApis, cryptoCallbacks, this._crossSigningInfo,
+ baseApis, cryptoCallbacks,
);
// Assuming no app-supplied callback, default to getting from SSSS.
@@ -257,6 +265,7 @@ utils.inherits(Crypto, EventEmitter);
Crypto.prototype.init = async function(opts) {
const {
exportedOlmDevice,
+ pickleKey,
} = opts || {};
logger.log("Crypto: initialising Olm...");
@@ -266,7 +275,7 @@ Crypto.prototype.init = async function(opts) {
? "Crypto: initialising Olm device from exported device..."
: "Crypto: initialising Olm device...",
);
- await this._olmDevice.init({ fromExportedDevice: exportedOlmDevice });
+ await this._olmDevice.init({ fromExportedDevice: exportedOlmDevice, pickleKey });
logger.log("Crypto: loading device list...");
await this._deviceList.load();
@@ -306,7 +315,8 @@ Crypto.prototype.init = async function(opts) {
'readonly', [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
this._cryptoStore.getCrossSigningKeys(txn, (keys) => {
- if (keys) {
+ // can be an empty object after resetting cross-signing keys, see _storeTrustedSelfKeys
+ if (keys && Object.keys(keys).length !== 0) {
logger.log("Loaded cross-signing public keys from crypto store");
this._crossSigningInfo.setKeys(keys);
}
@@ -432,6 +442,14 @@ Crypto.prototype.isCrossSigningReady = async function() {
* up, then no changes are made, so this is safe to run to ensure secret storage
* is ready for use.
*
+ * This function
+ * - creates a new Secure Secret Storage key if no default key exists
+ * - if a key backup exists, it is migrated to store the key in the Secret
+ * Storage
+ * - creates a backup if none exists, and one is requested
+ * - migrates Secure Secret Storage to use the latest algorithm, if an outdated
+ * algorithm is found
+ *
* @param {function} [opts.authUploadDeviceSigningKeys] Optional. Function
* called to await an interactive auth flow when uploading device signing keys.
* Args:
@@ -501,172 +519,160 @@ Crypto.prototype.bootstrapSecretStorage = async function({
return key;
};
+ // create a new SSSS key and set it as default
+ const createSSSS = async (opts, privateKey) => {
+ opts = opts || {};
+ if (privateKey) {
+ opts.key = privateKey;
+ }
+
+ const keyId = await this.addSecretStorageKey(
+ SECRET_STORAGE_ALGORITHM_V1_AES, opts,
+ );
+ await this.setDefaultSecretStorageKeyId(keyId);
+
+ if (privateKey) {
+ // cache the private key so that we can access it again
+ ssssKeys[keyId] = privateKey;
+ }
+ return keyId;
+ };
+
+ // reset the cross-signing keys
+ const resetCrossSigning = async () => {
+ this._baseApis._cryptoCallbacks.saveCrossSigningKeys =
+ keys => Object.assign(crossSigningPrivateKeys, keys);
+ this._baseApis._cryptoCallbacks.getCrossSigningKey =
+ name => crossSigningPrivateKeys[name];
+ await this.resetCrossSigningKeys(
+ CrossSigningLevel.MASTER,
+ { authUploadDeviceSigningKeys },
+ );
+ };
+
+ const ensureCanCheckPassphrase = async (keyId, keyInfo) => {
+ if (!keyInfo.mac) {
+ const key = await this._baseApis._cryptoCallbacks.getSecretStorageKey(
+ {keys: {[keyId]: keyInfo}}, "",
+ );
+ if (key) {
+ const keyData = key[1];
+ ssssKeys[keyId] = keyData;
+ const {iv, mac} = await SecretStorage._calculateKeyCheck(keyData);
+ keyInfo.iv = iv;
+ keyInfo.mac = mac;
+
+ await this._baseApis.setAccountData(
+ `m.secret_storage.key.${keyId}`, keyInfo,
+ );
+ }
+ }
+ };
+
try {
+ const oldSSSSKey = await this.getSecretStorageKey();
+ const [oldKeyId, oldKeyInfo] = oldSSSSKey || [null, null];
const decryptionKeys =
await this._crossSigningInfo.isStoredInSecretStorage(this._secretStorage);
const inStorage = !setupNewSecretStorage && decryptionKeys;
- if (decryptionKeys && !(Object.values(decryptionKeys).some(
- info => info.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES,
- ))) {
- // we already have cross-signing keys, but they're encrypted using
- // the old algorithm
- logger.log("Switching to symmetric");
- const keys = {};
- // fetch the cross-signing private keys (needed to sign the new
- // SSSS key). We store the cross-signing keys, and temporarily set
- // a callback so that when the private key is needed while setting
- // things up, we can provide it.
- this._baseApis._cryptoCallbacks.getCrossSigningKey =
- name => crossSigningPrivateKeys[name];
- for (const type of ["master", "self_signing", "user_signing"]) {
- const secretName = `m.cross_signing.${type}`;
- const secret = await this.getSecret(secretName);
- keys[type] = secret;
- crossSigningPrivateKeys[type] = olmlib.decodeBase64(secret);
- }
- await this.checkOwnCrossSigningTrust();
- const opts = {};
- let oldKeyId = null;
- for (const [keyId, keyInfo] of Object.entries(decryptionKeys)) {
- // See if the old key was generated from a passphrase. If
- // yes, use the same settings.
- if (keyId in ssssKeys) {
- oldKeyId = keyId;
- if (keyInfo.passphrase) {
- opts.passphrase = keyInfo.passphrase;
- }
- break;
- }
- }
- if (oldKeyId) {
- opts.key = ssssKeys[oldKeyId];
- }
- // create new symmetric SSSS key and set it as default
- newKeyId = await this.addSecretStorageKey(
- SECRET_STORAGE_ALGORITHM_V1_AES, opts,
+
+ if (!inStorage && !keyBackupInfo) {
+ // either we don't have anything, or we've been asked to restart
+ // from scratch
+ logger.log(
+ "Cross-signing private keys not found in secret storage, " +
+ "creating new keys",
);
- if (oldKeyId) {
- ssssKeys[newKeyId] = ssssKeys[oldKeyId];
+
+ await resetCrossSigning();
+
+ if (
+ setupNewSecretStorage ||
+ !oldKeyInfo ||
+ oldKeyInfo.algorithm !== SECRET_STORAGE_ALGORITHM_V1_AES
+ ) {
+ // if we already have a usable default SSSS key and aren't resetting SSSS just use it.
+ // otherwise, create a new one
+ // Note: we leave the old SSSS key in place: there could be other secrets using it, in theory.
+ // We could move them to the new key but a) that would mean we'd need to prompt for the old
+ // passphrase, and b) it's not clear that would be the right thing to do anyway.
+ const { keyInfo, privateKey } = await createSecretStorageKey();
+ newKeyId = await createSSSS(keyInfo, privateKey);
}
- await this.setDefaultSecretStorageKeyId(newKeyId);
- // re-encrypt all the keys with the new key
- for (const type of ["master", "self_signing", "user_signing"]) {
- const secretName = `m.cross_signing.${type}`;
- await this.storeSecret(secretName, keys[type], [newKeyId]);
+
+ if (oldKeyInfo && oldKeyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
+ await ensureCanCheckPassphrase(oldKeyId, oldKeyInfo);
}
- } else if (!this._crossSigningInfo.getId() || !inStorage) {
- // create new cross-signing keys if necessary.
- logger.log(
- "Cross-signing public and/or private keys not found, " +
- "checking secret storage for private keys",
- );
- if (inStorage) {
- logger.log("Cross-signing private keys found in secret storage");
- await this.checkOwnCrossSigningTrust();
- } else {
- logger.log(
- "Cross-signing private keys not found in secret storage, " +
- "creating new keys",
- );
- this._baseApis._cryptoCallbacks.saveCrossSigningKeys =
- keys => Object.assign(crossSigningPrivateKeys, keys);
- this._baseApis._cryptoCallbacks.getCrossSigningKey =
- name => crossSigningPrivateKeys[name];
- await this.resetCrossSigningKeys(
- CrossSigningLevel.MASTER,
- { authUploadDeviceSigningKeys },
- );
+ } else if (!inStorage && keyBackupInfo) {
+ // we have an existing backup, but no SSSS
+
+ logger.log("Secret storage default key not found, using key backup key");
+
+ // if we have the backup key already cached, use it; otherwise use the
+ // callback to prompt for the key
+ const backupKey = await this.getSessionBackupPrivateKey() ||
+ await getKeyBackupPassphrase();
+
+ // create new cross-signing keys
+ await resetCrossSigning();
+
+ // create a new SSSS key and use the backup key as the new SSSS key
+ const opts = {};
+
+ if (
+ keyBackupInfo.auth_data.private_key_salt &&
+ keyBackupInfo.auth_data.private_key_iterations
+ ) {
+ opts.passphrase = {
+ algorithm: "m.pbkdf2",
+ iterations: keyBackupInfo.auth_data.private_key_iterations,
+ salt: keyBackupInfo.auth_data.private_key_salt,
+ bits: 256,
+ };
}
- } else {
- logger.log("Cross signing keys are present in secret storage");
- }
- // Check if we need to create a new secret storage key
- // - we're resetting secret storage
- // - we don't have a default secret storage key yet
- // - our default secret storage key is using an older algorithm
- // We will also run this part if we created a new secret storage key
- // above, so that we can (re-)encrypt the backup with it.
- const defaultSSSSKey = await this.getSecretStorageKey();
- if (setupNewSecretStorage || newKeyId || !defaultSSSSKey
- || defaultSSSSKey[1].algorithm !== SECRET_STORAGE_ALGORITHM_V1_AES) {
- if (keyBackupInfo) {
- // if we already have a backup key, use the same key as the
- // secret storage key
- logger.log("Secret storage default key not found, using key backup key");
-
- const backupKey = await getKeyBackupPassphrase();
-
- if (!newKeyId) {
- const opts = {};
+ newKeyId = await createSSSS(opts, backupKey);
- if (
- keyBackupInfo.auth_data.private_key_salt &&
- keyBackupInfo.auth_data.private_key_iterations
- ) {
- opts.passphrase = {
- algorithm: "m.pbkdf2",
- iterations: keyBackupInfo.auth_data.private_key_iterations,
- salt: keyBackupInfo.auth_data.private_key_salt,
- bits: 256,
- };
- }
+ // store the backup key in secret storage
+ await this.storeSecret(
+ "m.megolm_backup.v1", olmlib.encodeBase64(backupKey), [newKeyId],
+ );
- // use the backup key as the new ssss key
- ssssKeys[newKeyId] = backupKey;
- opts.key = backupKey;
+ // The backup is trusted because the user provided the private key.
+ // Sign the backup with the cross signing key so the key backup can
+ // be trusted via cross-signing.
+ logger.log("Adding cross signing signature to key backup");
+ await this._crossSigningInfo.signObject(
+ keyBackupInfo.auth_data, "master",
+ );
+ await this._baseApis._http.authedRequest(
+ undefined, "PUT", "/room_keys/version/" + keyBackupInfo.version,
+ undefined, keyBackupInfo,
+ {prefix: httpApi.PREFIX_UNSTABLE},
+ );
+ } else if (!this._crossSigningInfo.getId()) {
+ // we have SSSS, but we don't know if the server's cross-signing
+ // keys should be trusted
+ logger.log("Cross-signing private keys found in secret storage");
- newKeyId = await this.addSecretStorageKey(
- SECRET_STORAGE_ALGORITHM_V1_AES, opts,
- );
- await this.setDefaultSecretStorageKeyId(newKeyId);
- }
+ // fetch the private keys and set up our local copy of the keys for
+ // use
+ await this.checkOwnCrossSigningTrust();
- // if this key backup is trusted, sign it with the cross signing key
- // so the key backup can be trusted via cross-signing.
- const backupSigStatus = await this.checkKeyBackup(keyBackupInfo);
- if (backupSigStatus.trustInfo.usable) {
- logger.log("Adding cross signing signature to key backup");
- await this._crossSigningInfo.signObject(
- keyBackupInfo.auth_data, "master",
- );
- await this._baseApis._http.authedRequest(
- undefined, "PUT", "/room_keys/version/" + keyBackupInfo.version,
- undefined, keyBackupInfo,
- {prefix: httpApi.PREFIX_UNSTABLE},
- );
- await this.storeSecret(
- "m.megolm_backup.v1", olmlib.encodeBase64(backupKey), [newKeyId],
- );
- } else {
- logger.log(
- "Key backup is NOT TRUSTED: NOT adding cross signing signature",
- );
- }
- } else {
- if (!newKeyId) {
- logger.log("Secret storage default key not found, creating new key");
- const { keyInfo, privateKey } = await createSecretStorageKey();
- if (keyInfo && privateKey) {
- keyInfo.key = privateKey;
- }
- newKeyId = await this.addSecretStorageKey(
- SECRET_STORAGE_ALGORITHM_V1_AES,
- keyInfo,
- );
- await this.setDefaultSecretStorageKeyId(newKeyId);
- ssssKeys[newKeyId] = privateKey;
- }
- if (await this.isSecretStored("m.megolm_backup.v1")) {
- // we created a new SSSS, and we previously encrypted the
- // backup key with the old SSSS key, so re-encrypt with the
- // new key
- const backupKey = await this.getSecret("m.megolm_backup.v1");
- await this.storeSecret("m.megolm_backup.v1", backupKey, [newKeyId]);
- }
+ if (oldKeyInfo && oldKeyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
+ // make sure that the default key has the information needed to
+ // check the passphrase
+ await ensureCanCheckPassphrase(oldKeyId, oldKeyInfo);
}
} else {
- logger.log("Have secret storage key");
+ // we have SSSS and we cross-signing is already set up
+ logger.log("Cross signing keys are present in secret storage");
+
+ if (oldKeyInfo && oldKeyInfo.algorithm === SECRET_STORAGE_ALGORITHM_V1_AES) {
+ // make sure that the default key has the information needed to
+ // check the passphrase
+ await ensureCanCheckPassphrase(oldKeyId, oldKeyInfo);
+ }
}
// If cross-signing keys were reset, store them in Secure Secret Storage.
@@ -676,11 +682,6 @@ Crypto.prototype.bootstrapSecretStorage = async function({
// See also https://github.com/vector-im/riot-web/issues/11635
if (Object.keys(crossSigningPrivateKeys).length) {
logger.log("Storing cross-signing private keys in secret storage");
- // SSSS expects its keys to be signed by cross-signing master key.
- // Since we have just reset cross-signing keys, we need to re-sign the
- // SSSS default key with the new cross-signing master key so that the
- // following storage step can proceed.
- await this._secretStorage.signKey();
// Assuming no app-supplied callback, default to storing in SSSS.
if (!appCallbacks.saveCrossSigningKeys) {
await CrossSigningInfo.storeInSecretStorage(
@@ -711,8 +712,9 @@ Crypto.prototype.bootstrapSecretStorage = async function({
const sessionBackupKey = await this.getSecret('m.megolm_backup.v1');
if (sessionBackupKey) {
logger.info("Got session backup key from secret storage: caching");
- const decoded = olmlib.decodeBase64(sessionBackupKey);
- await this.storeSessionBackupPrivateKey(decoded);
+ const decodedBackupKey =
+ new Uint8Array(olmlib.decodeBase64(sessionBackupKey));
+ await this.storeSessionBackupPrivateKey(decodedBackupKey);
}
} finally {
// Restore the original callbacks. NB. we must do this by manipulating
@@ -737,10 +739,6 @@ Crypto.prototype.hasSecretStorageKey = function(keyID) {
return this._secretStorage.hasKey(keyID);
};
-Crypto.prototype.secretStorageKeyNeedsUpgrade = function(keyID) {
- return this._secretStorage.keyNeedsUpgrade(keyID);
-};
-
Crypto.prototype.getSecretStorageKey = function(keyID) {
return this._secretStorage.getKey(keyID);
};
@@ -802,7 +800,7 @@ Crypto.prototype.checkSecretStoragePrivateKey = function(privateKey, expectedPub
* @returns {Promise} the key, if any, or null
*/
Crypto.prototype.getSessionBackupPrivateKey = async function() {
- return new Promise((resolve) => {
+ const key = await new Promise((resolve) => {
this._cryptoStore.doTxn(
'readonly',
[IndexedDBCryptoStore.STORE_ACCOUNT],
@@ -815,6 +813,14 @@ Crypto.prototype.getSessionBackupPrivateKey = async function() {
},
);
});
+
+ if (key && key.ciphertext) {
+ const pickleKey = Buffer.from(this._olmDevice._pickleKey);
+ const decrypted = await decryptAES(key, pickleKey, "m.megolm_backup.v1");
+ return olmlib.decodeBase64(decrypted);
+ } else {
+ return key;
+ }
};
/**
@@ -826,6 +832,8 @@ Crypto.prototype.storeSessionBackupPrivateKey = async function(key) {
if (!(key instanceof Uint8Array)) {
throw new Error(`storeSessionBackupPrivateKey expects Uint8Array, got ${key}`);
}
+ const pickleKey = Buffer.from(this._olmDevice._pickleKey);
+ key = await encryptAES(olmlib.encodeBase64(key), pickleKey, "m.megolm_backup.v1");
return this._cryptoStore.doTxn(
'readwrite',
[IndexedDBCryptoStore.STORE_ACCOUNT],
@@ -1145,13 +1153,18 @@ Crypto.prototype._onDeviceListUserCrossSigningUpdated = async function(userId) {
// If it's not changed, just make sure everything is up to date
await this.checkOwnCrossSigningTrust();
} else {
- this.emit("crossSigning.keysChanged", {});
// We'll now be in a state where cross-signing on the account is not trusted
// because our locally stored cross-signing keys will not match the ones
- // on the server for our account. The app must call checkOwnCrossSigningTrust()
- // to fix this.
- // XXX: Do we need to do something to emit events saying every device has become
- // untrusted?
+ // on the server for our account. So we clear our own stored cross-signing keys,
+ // effectively disabling cross-signing until the user gets verified by the device
+ // that reset the keys
+ this._storeTrustedSelfKeys(null);
+ // emit cross-signing has been disabled
+ this.emit("crossSigning.keysChanged", {});
+ // as the trust for our own user has changed,
+ // also emit an event for this
+ this.emit("userTrustStatusChanged",
+ this._userId, this.checkUserTrust(userId));
}
} else {
await this._checkDeviceVerifications(userId);
@@ -1307,7 +1320,11 @@ Crypto.prototype.checkOwnCrossSigningTrust = async function() {
* @param {object} keys The new trusted set of keys
*/
Crypto.prototype._storeTrustedSelfKeys = async function(keys) {
- this._crossSigningInfo.setKeys(keys);
+ if (keys) {
+ this._crossSigningInfo.setKeys(keys);
+ } else {
+ this._crossSigningInfo.clearKeys();
+ }
await this._cryptoStore.doTxn(
'readwrite', [IndexedDBCryptoStore.STORE_ACCOUNT],
(txn) => {
@@ -1372,8 +1389,9 @@ Crypto.prototype._checkAndStartKeyBackup = async function() {
backupInfo = await this._baseApis.getKeyBackupVersion();
} catch (e) {
logger.log("Error checking for active key backup", e);
- if (e.httpStatus / 100 === 4) {
- // well that's told us. we won't try again.
+ if (e.httpStatus === 404) {
+ // 404 is returned when the key backup does not exist, so that
+ // counts as successfully checking.
this._checkedForBackup = true;
}
return null;
@@ -1916,6 +1934,10 @@ Crypto.prototype.setDeviceVerification = async function(
if (!this._crossSigningInfo.getId() && userId === this._crossSigningInfo.userId) {
this._storeTrustedSelfKeys(xsk.keys);
+ // This will cause our own user trust to change, so emit the event
+ this.emit(
+ "userTrustStatusChanged", this._userId, this.checkUserTrust(userId),
+ );
}
// Now sign the master key with our user signing key (unless it's ourself)
@@ -2058,7 +2080,8 @@ Crypto.prototype.requestVerification = function(userId, devices) {
if (existingRequest) {
return Promise.resolve(existingRequest);
}
- const channel = new ToDeviceChannel(this._baseApis, userId, devices);
+ const channel = new ToDeviceChannel(this._baseApis, userId, devices,
+ ToDeviceChannel.makeTransactionId());
return this._requestVerificationWithChannel(
userId,
channel,
@@ -2071,6 +2094,10 @@ Crypto.prototype._requestVerificationWithChannel = async function(
) {
let request = new VerificationRequest(
channel, this._verificationMethods, this._baseApis);
+ // if transaction id is already known, add request
+ if (channel.transactionId) {
+ requestsMap.setRequestByChannel(channel, request);
+ }
await request.sendRequest();
// don't replace the request created by a racing remote echo
const racingRequest = requestsMap.getRequestByChannel(channel);
@@ -2438,17 +2465,37 @@ Crypto.prototype.exportRoomKeys = async function() {
* Import a list of room keys previously exported by exportRoomKeys
*
* @param {Object[]} keys a list of session export objects
+ * @param {Object} opts
+ * @param {Function} opts.progressCallback called with an object which has a stage param
* @return {Promise} a promise which resolves once the keys have been imported
*/
-Crypto.prototype.importRoomKeys = function(keys) {
+Crypto.prototype.importRoomKeys = function(keys, opts = {}) {
+ let successes = 0;
+ let failures = 0;
+ const total = keys.length;
+
+ function updateProgress() {
+ opts.progressCallback({
+ stage: "load_keys",
+ successes,
+ failures,
+ total,
+ });
+ }
+
return Promise.all(keys.map((key) => {
if (!key.room_id || !key.algorithm) {
logger.warn("ignoring room key entry with missing fields", key);
+ failures++;
+ if (opts.progressCallback) { updateProgress(); }
return null;
}
const alg = this._getRoomDecryptor(key.room_id, key.algorithm);
- return alg.importRoomKey(key);
+ return alg.importRoomKey(key).finally((r) => {
+ successes++;
+ if (opts.progressCallback) { updateProgress(); }
+ });
}));
};
@@ -2764,9 +2811,13 @@ Crypto.prototype.handleDeviceListChanges = async function(syncData, syncDeviceLi
* @return {Promise} a promise that resolves when the key request is queued
*/
Crypto.prototype.requestRoomKey = function(requestBody, recipients, resend=false) {
- return this._outgoingRoomKeyRequestManager.sendRoomKeyRequest(
+ return this._outgoingRoomKeyRequestManager.queueRoomKeyRequest(
requestBody, recipients, resend,
- ).catch((e) => {
+ ).then(() => {
+ if (this._sendKeyRequestsImmediately) {
+ this._outgoingRoomKeyRequestManager.sendQueuedRequests();
+ }
+ }).catch((e) => {
// this normally means we couldn't talk to the store
logger.error(
'Error requesting key for event', e,
@@ -2831,6 +2882,8 @@ Crypto.prototype.onSyncWillProcess = async function(syncData) {
this._deviceList.startTrackingDeviceList(this._userId);
this._roomDeviceTrackingState = {};
}
+
+ this._sendKeyRequestsImmediately = false;
};
/**
@@ -2862,6 +2915,14 @@ Crypto.prototype.onSyncCompleted = async function(syncData) {
if (!syncData.catchingUp) {
_maybeUploadOneTimeKeys(this);
this._processReceivedRoomKeyRequests();
+
+ // likewise don't start requesting keys until we've caught up
+ // on to_device messages, otherwise we'll request keys that we're
+ // just about to get.
+ this._outgoingRoomKeyRequestManager.sendQueuedRequests();
+
+ // Sync has finished so send key requests straight away.
+ this._sendKeyRequestsImmediately = true;
}
};
@@ -3381,6 +3442,19 @@ Crypto.prototype._processReceivedRoomKeyRequest = async function(req) {
return;
}
+ if (deviceId === this._deviceId) {
+ // We'll always get these because we send room key requests to
+ // '*' (ie. 'all devices') which includes the sending device,
+ // so ignore requests from ourself because apart from it being
+ // very silly, it won't work because an Olm session cannot send
+ // messages to itself.
+ // The log here is probably superfluous since we know this will
+ // always happen, but let's log anyway for now just in case it
+ // causes issues.
+ logger.log("Ignoring room key request from ourselves");
+ return;
+ }
+
// todo: should we queue up requests we don't yet have keys for,
// in case they turn up later?
diff --git a/src/crypto/olmlib.js b/src/crypto/olmlib.js
index fd8bee94..dbdf07db 100644
--- a/src/crypto/olmlib.js
+++ b/src/crypto/olmlib.js
@@ -207,6 +207,25 @@ export async function ensureOlmSessionsForDevices(
for (const deviceInfo of devices) {
const deviceId = deviceInfo.deviceId;
const key = deviceInfo.getIdentityKey();
+
+ if (key === olmDevice.deviceCurve25519Key) {
+ // We should never be trying to start a session with ourself.
+ // Apart from talking to yourself being the first sign of madness,
+ // olm sessions can't do this because they get confused when
+ // they get a message and see that the 'other side' has started a
+ // new chain when this side has an active sender chain.
+ // If you see this message being logged in the wild, we should find
+ // the thing that is trying to send Olm messages to itself and fix it.
+ logger.info("Attempted to start session with ourself! Ignoring");
+ // We must fill in the section in the return value though, as callers
+ // expect it to be there.
+ result[userId][deviceId] = {
+ device: deviceInfo,
+ sessionId: null,
+ };
+ continue;
+ }
+
if (!olmDevice._sessionsInProgress[key]) {
// pre-emptively mark the session as in-progress to avoid race
// conditions. If we find that we already have a session, then
@@ -238,6 +257,11 @@ export async function ensureOlmSessionsForDevices(
delete resolveSession[key];
}
if (sessionId === null || force) {
+ if (force) {
+ logger.info("Forcing new Olm session for " + userId + ":" + deviceId);
+ } else {
+ logger.info("Making new Olm session for " + userId + ":" + deviceId);
+ }
devicesWithoutSession.push([userId, deviceId]);
}
result[userId][deviceId] = {
@@ -277,6 +301,14 @@ export async function ensureOlmSessionsForDevices(
const deviceInfo = devices[j];
const deviceId = deviceInfo.deviceId;
const key = deviceInfo.getIdentityKey();
+
+ if (key === olmDevice.deviceCurve25519Key) {
+ // We've already logged about this above. Skip here too
+ // otherwise we'll log saying there are no one-time keys
+ // which will be confusing.
+ continue;
+ }
+
if (result[userId][deviceId].sessionId && !force) {
// we already have a result for this device
continue;
diff --git a/src/crypto/store/localStorage-crypto-store.js b/src/crypto/store/localStorage-crypto-store.js
index 1e63affe..3e673168 100644
--- a/src/crypto/store/localStorage-crypto-store.js
+++ b/src/crypto/store/localStorage-crypto-store.js
@@ -369,7 +369,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore {
getSecretStorePrivateKey(txn, func, type) {
const key = getJsonItem(this.store, E2E_PREFIX + `ssss_cache.${type}`);
- func(key ? Uint8Array.from(key) : key);
+ func(key);
}
storeCrossSigningKeys(txn, keys) {
@@ -380,7 +380,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore {
storeSecretStorePrivateKey(txn, type, key) {
setJsonItem(
- this.store, E2E_PREFIX + `ssss_cache.${type}`, Array.from(key),
+ this.store, E2E_PREFIX + `ssss_cache.${type}`, key,
);
}
diff --git a/src/crypto/verification/Base.js b/src/crypto/verification/Base.js
index 33284086..0b6cf31d 100644
--- a/src/crypto/verification/Base.js
+++ b/src/crypto/verification/Base.js
@@ -122,6 +122,11 @@ export class VerificationBase extends EventEmitter {
if (this._done) {
return Promise.reject(new Error("Verification is already done"));
}
+ const existingEvent = this.request.getEventFromOtherParty(type);
+ if (existingEvent) {
+ return Promise.resolve(existingEvent);
+ }
+
this._expectedEvent = type;
return new Promise((resolve, reject) => {
this._resolveEvent = resolve;
@@ -287,6 +292,7 @@ export class VerificationBase extends EventEmitter {
this._endTimer(); // always kill the activity timer
if (!this._done) {
this.cancelled = true;
+ this.request.onVerifierCancelled();
if (this.userId && this.deviceId) {
// send a cancellation to the other user (if it wasn't
// cancelled by the other user)
@@ -369,7 +375,7 @@ export class VerificationBase extends EventEmitter {
for (const [keyId, keyInfo] of Object.entries(keys)) {
const deviceId = keyId.split(':', 2)[1];
- const device = await this._baseApis.getStoredDevice(userId, deviceId);
+ const device = this._baseApis.getStoredDevice(userId, deviceId);
if (device) {
await verifier(keyId, device, keyInfo);
verifiedDevices.push(deviceId);
diff --git a/src/crypto/verification/QRCode.js b/src/crypto/verification/QRCode.js
index 34d51016..7be1c06a 100644
--- a/src/crypto/verification/QRCode.js
+++ b/src/crypto/verification/QRCode.js
@@ -65,20 +65,29 @@ export class ReciprocateQRCode extends Base {
this.emit("show_reciprocate_qr", this.reciprocateQREvent);
});
- // 3. determine key to sign
+ // 3. determine key to sign / mark as trusted
const keys = {};
- if (qrCodeData.mode === MODE_VERIFY_OTHER_USER) {
- // add master key to keys to be signed, only if we're not doing self-verification
- const masterKey = qrCodeData.otherUserMasterKey;
- keys[`ed25519:${masterKey}`] = masterKey;
- } else if (qrCodeData.mode === MODE_VERIFY_SELF_TRUSTED) {
- const deviceId = this.request.targetDevice.deviceId;
- keys[`ed25519:${deviceId}`] = qrCodeData.otherDeviceKey;
- } else {
- // TODO: not sure if MODE_VERIFY_SELF_UNTRUSTED makes sense to sign anything here?
+
+ switch (qrCodeData.mode) {
+ case MODE_VERIFY_OTHER_USER: {
+ // add master key to keys to be signed, only if we're not doing self-verification
+ const masterKey = qrCodeData.otherUserMasterKey;
+ keys[`ed25519:${masterKey}`] = masterKey;
+ break;
+ }
+ case MODE_VERIFY_SELF_TRUSTED: {
+ const deviceId = this.request.targetDevice.deviceId;
+ keys[`ed25519:${deviceId}`] = qrCodeData.otherDeviceKey;
+ break;
+ }
+ case MODE_VERIFY_SELF_UNTRUSTED: {
+ const masterKey = qrCodeData.myMasterKey;
+ keys[`ed25519:${masterKey}`] = masterKey;
+ break;
+ }
}
- // 4. sign the key
+ // 4. sign the key (or mark own MSK as verified in case of MODE_VERIFY_SELF_TRUSTED)
await this._verifyKeys(this.userId, keys, (keyId, device, keyInfo) => {
// make sure the device has the expected keys
const targetKey = keys[keyId];
@@ -108,11 +117,15 @@ const MODE_VERIFY_SELF_TRUSTED = 0x01; // We trust the master key
const MODE_VERIFY_SELF_UNTRUSTED = 0x02; // We do not trust the master key
export class QRCodeData {
- constructor(mode, sharedSecret, otherUserMasterKey, otherDeviceKey, buffer) {
+ constructor(
+ mode, sharedSecret, otherUserMasterKey,
+ otherDeviceKey, myMasterKey, buffer,
+ ) {
this._sharedSecret = sharedSecret;
this._mode = mode;
this._otherUserMasterKey = otherUserMasterKey;
this._otherDeviceKey = otherDeviceKey;
+ this._myMasterKey = myMasterKey;
this._buffer = buffer;
}
@@ -121,22 +134,28 @@ export class QRCodeData {
const mode = QRCodeData._determineMode(request, client);
let otherUserMasterKey = null;
let otherDeviceKey = null;
+ let myMasterKey = null;
if (mode === MODE_VERIFY_OTHER_USER) {
const otherUserCrossSigningInfo =
client.getStoredCrossSigningForUser(request.otherUserId);
otherUserMasterKey = otherUserCrossSigningInfo.getId("master");
} else if (mode === MODE_VERIFY_SELF_TRUSTED) {
otherDeviceKey = await QRCodeData._getOtherDeviceKey(request, client);
+ } else if (mode === MODE_VERIFY_SELF_UNTRUSTED) {
+ const myUserId = client.getUserId();
+ const myCrossSigningInfo = client.getStoredCrossSigningForUser(myUserId);
+ myMasterKey = myCrossSigningInfo.getId("master");
}
const qrData = QRCodeData._generateQrData(
request, client, mode,
sharedSecret,
otherUserMasterKey,
otherDeviceKey,
+ myMasterKey,
);
const buffer = QRCodeData._generateBuffer(qrData);
return new QRCodeData(mode, sharedSecret,
- otherUserMasterKey, otherDeviceKey, buffer);
+ otherUserMasterKey, otherDeviceKey, myMasterKey, buffer);
}
get buffer() {
@@ -147,15 +166,31 @@ export class QRCodeData {
return this._mode;
}
+ /**
+ * only set when mode is MODE_VERIFY_SELF_TRUSTED
+ * @return {string} device key of other party at time of generating QR code
+ */
get otherDeviceKey() {
return this._otherDeviceKey;
}
+ /**
+ * only set when mode is MODE_VERIFY_OTHER_USER
+ * @return {string} master key of other party at time of generating QR code
+ */
get otherUserMasterKey() {
return this._otherUserMasterKey;
}
/**
+ * only set when mode is MODE_VERIFY_SELF_UNTRUSTED
+ * @return {string} own master key at time of generating QR code
+ */
+ get myMasterKey() {
+ return this._myMasterKey;
+ }
+
+ /**
* The unpadded base64 encoded shared secret.
*/
get encodedSharedSecret() {
@@ -172,7 +207,7 @@ export class QRCodeData {
const myUserId = client.getUserId();
const otherDevice = request.targetDevice;
const otherDeviceId = otherDevice ? otherDevice.deviceId : null;
- const device = await client.getStoredDevice(myUserId, otherDeviceId);
+ const device = client.getStoredDevice(myUserId, otherDeviceId);
if (!device) {
throw new Error("could not find device " + otherDeviceId);
}
@@ -198,7 +233,8 @@ export class QRCodeData {
}
static _generateQrData(request, client, mode,
- encodedSharedSecret, otherUserMasterKey, otherDeviceKey,
+ encodedSharedSecret, otherUserMasterKey,
+ otherDeviceKey, myMasterKey,
) {
const myUserId = client.getUserId();
const transactionId = request.channel.transactionId;
@@ -213,16 +249,15 @@ export class QRCodeData {
};
const myCrossSigningInfo = client.getStoredCrossSigningForUser(myUserId);
- const myMasterKey = myCrossSigningInfo.getId("master");
if (mode === MODE_VERIFY_OTHER_USER) {
// First key is our master cross signing key
- qrData.firstKeyB64 = myMasterKey;
+ qrData.firstKeyB64 = myCrossSigningInfo.getId("master");
// Second key is the other user's master cross signing key
qrData.secondKeyB64 = otherUserMasterKey;
} else if (mode === MODE_VERIFY_SELF_TRUSTED) {
// First key is our master cross signing key
- qrData.firstKeyB64 = myMasterKey;
+ qrData.firstKeyB64 = myCrossSigningInfo.getId("master");
qrData.secondKeyB64 = otherDeviceKey;
} else if (mode === MODE_VERIFY_SELF_UNTRUSTED) {
// First key is our device's key
diff --git a/src/crypto/verification/SAS.js b/src/crypto/verification/SAS.js
index a00ab3d7..7aba7622 100644
--- a/src/crypto/verification/SAS.js
+++ b/src/crypto/verification/SAS.js
@@ -175,11 +175,33 @@ function calculateMAC(olmSAS, method) {
};
}
+const calculateKeyAgreement = {
+ "curve25519-hkdf-sha256": function(sas, olmSAS, bytes) {
+ const ourInfo = `${sas._baseApis.getUserId()}|${sas._baseApis.deviceId}|`
+ + `${sas.ourSASPubKey}|`;
+ const theirInfo = `${sas.userId}|${sas.deviceId}|${sas.theirSASPubKey}|`;
+ const sasInfo =
+ "MATRIX_KEY_VERIFICATION_SAS|"
+ + (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo)
+ + sas._channel.transactionId;
+ return olmSAS.generate_bytes(sasInfo, bytes);
+ },
+ "curve25519": function(sas, olmSAS, bytes) {
+ const ourInfo = `${sas._baseApis.getUserId()}${sas._baseApis.deviceId}`;
+ const theirInfo = `${sas.userId}${sas.deviceId}`;
+ const sasInfo =
+ "MATRIX_KEY_VERIFICATION_SAS"
+ + (sas.initiatedByMe ? ourInfo + theirInfo : theirInfo + ourInfo)
+ + sas._channel.transactionId;
+ return olmSAS.generate_bytes(sasInfo, bytes);
+ },
+};
+
/* lists of algorithms/methods that are supported. The key agreement, hashes,
* and MAC lists should be sorted in order of preference (most preferred
* first).
*/
-const KEY_AGREEMENT_LIST = ["curve25519"];
+const KEY_AGREEMENT_LIST = ["curve25519-hkdf-sha256", "curve25519"];
const HASHES_LIST = ["sha256"];
const MAC_LIST = ["hkdf-hmac-sha256", "hmac-sha256"];
const SAS_LIST = Object.keys(sasGenerators);
@@ -291,12 +313,14 @@ export class SAS extends Base {
if (typeof content.commitment !== "string") {
throw newInvalidMessageError();
}
+ const keyAgreement = content.key_agreement_protocol;
const macMethod = content.message_authentication_code;
const hashCommitment = content.commitment;
const olmSAS = new global.Olm.SAS();
try {
- this._send("m.key.verification.key", {
- key: olmSAS.get_pubkey(),
+ this.ourSASPubKey = olmSAS.get_pubkey();
+ await this._send("m.key.verification.key", {
+ key: this.ourSASPubKey,
});
@@ -308,19 +332,20 @@ export class SAS extends Base {
if (olmutil.sha256(commitmentStr) !== hashCommitment) {
throw newMismatchedCommitmentError();
}
+ this.theirSASPubKey = content.key;
olmSAS.set_their_key(content.key);
- const sasInfo = "MATRIX_KEY_VERIFICATION_SAS"
- + this._baseApis.getUserId() + this._baseApis.deviceId
- + this.userId + this.deviceId
- + this._channel.transactionId;
- const sasBytes = olmSAS.generate_bytes(sasInfo, 6);
+ const sasBytes = calculateKeyAgreement[keyAgreement](this, olmSAS, 6);
const verifySAS = new Promise((resolve, reject) => {
this.sasEvent = {
sas: generateSas(sasBytes, sasMethods),
- confirm: () => {
- this._sendMAC(olmSAS, macMethod);
- resolve();
+ confirm: async () => {
+ try {
+ await this._sendMAC(olmSAS, macMethod);
+ resolve();
+ } catch (err) {
+ reject(err);
+ }
},
cancel: () => reject(newUserCancelledError()),
mismatch: () => reject(newMismatchedSASError()),
@@ -377,7 +402,7 @@ export class SAS extends Base {
const olmSAS = new global.Olm.SAS();
try {
const commitmentStr = olmSAS.get_pubkey() + anotherjson.stringify(content);
- this._send("m.key.verification.accept", {
+ await this._send("m.key.verification.accept", {
key_agreement_protocol: keyAgreement,
hash: hashMethod,
message_authentication_code: macMethod,
@@ -390,22 +415,24 @@ export class SAS extends Base {
let e = await this._waitForEvent("m.key.verification.key");
// FIXME: make sure event is properly formed
content = e.getContent();
+ this.theirSASPubKey = content.key;
olmSAS.set_their_key(content.key);
- this._send("m.key.verification.key", {
- key: olmSAS.get_pubkey(),
+ this.ourSASPubKey = olmSAS.get_pubkey();
+ await this._send("m.key.verification.key", {
+ key: this.ourSASPubKey,
});
- const sasInfo = "MATRIX_KEY_VERIFICATION_SAS"
- + this.userId + this.deviceId
- + this._baseApis.getUserId() + this._baseApis.deviceId
- + this._channel.transactionId;
- const sasBytes = olmSAS.generate_bytes(sasInfo, 6);
+ const sasBytes = calculateKeyAgreement[keyAgreement](this, olmSAS, 6);
const verifySAS = new Promise((resolve, reject) => {
this.sasEvent = {
sas: generateSas(sasBytes, sasMethods),
- confirm: () => {
- this._sendMAC(olmSAS, macMethod);
- resolve();
+ confirm: async () => {
+ try {
+ await this._sendMAC(olmSAS, macMethod);
+ resolve();
+ } catch(err) {
+ reject(err);
+ }
},
cancel: () => reject(newUserCancelledError()),
mismatch: () => reject(newMismatchedSASError()),
@@ -461,7 +488,7 @@ export class SAS extends Base {
keyList.sort().join(","),
baseInfo + "KEY_IDS",
);
- this._send("m.key.verification.mac", { mac, keys });
+ return this._send("m.key.verification.mac", { mac, keys });
}
async _checkMAC(olmSAS, content, method) {
diff --git a/src/crypto/verification/request/VerificationRequest.js b/src/crypto/verification/request/VerificationRequest.js
index afb0d51a..a77b8c96 100644
--- a/src/crypto/verification/request/VerificationRequest.js
+++ b/src/crypto/verification/request/VerificationRequest.js
@@ -25,15 +25,17 @@ import {
} from "../Error";
import {QRCodeData, SCAN_QR_CODE_METHOD} from "../QRCode";
-// the recommended amount of time before a verification request
-// should be (automatically) cancelled without user interaction
-// and ignored.
-const VERIFICATION_REQUEST_TIMEOUT = 10 * 60 * 1000; //10m
+// How long after the event's timestamp that the request times out
+const TIMEOUT_FROM_EVENT_TS = 10 * 60 * 1000; // 10 minutes
+
+// How long after we receive the event that the request times out
+const TIMEOUT_FROM_EVENT_RECEIPT = 2 * 60 * 1000; // 2 minutes
+
// to avoid almost expired verification notifications
// from showing a notification and almost immediately
// disappearing, also ignore verification requests that
// are this amount of time away from expiring.
-const VERIFICATION_REQUEST_MARGIN = 3 * 1000; //3s
+const VERIFICATION_REQUEST_MARGIN = 3 * 1000; // 3 seconds
export const EVENT_PREFIX = "m.key.verification.";
@@ -73,12 +75,16 @@ export class VerificationRequest extends EventEmitter {
this._accepting = false;
this._declining = false;
this._verifierHasFinished = false;
+ this._cancelled = false;
this._chosenMethod = null;
// we keep a copy of the QR Code data (including other user master key) around
// for QR reciprocate verification, to protect against
// cross-signing identity reset between the .ready and .start event
// and signing the wrong key after .start
this._qrCodeData = null;
+
+ // The timestamp when we received the request event from the other side
+ this._requestReceivedAt = null;
}
/**
@@ -164,12 +170,26 @@ export class VerificationRequest extends EventEmitter {
return this._chosenMethod;
}
+ calculateEventTimeout(event) {
+ let effectiveExpiresAt = this.channel.getTimestamp(event)
+ + TIMEOUT_FROM_EVENT_TS;
+
+ if (this._requestReceivedAt && !this.initiatedByMe &&
+ this.phase <= PHASE_REQUESTED
+ ) {
+ const expiresAtByReceipt = this._requestReceivedAt
+ + TIMEOUT_FROM_EVENT_RECEIPT;
+ effectiveExpiresAt = Math.min(effectiveExpiresAt, expiresAtByReceipt);
+ }
+
+ return Math.max(0, effectiveExpiresAt - Date.now());
+ }
+
/** The current remaining amount of ms before the request should be automatically cancelled */
get timeout() {
const requestEvent = this._getEventByEither(REQUEST_TYPE);
if (requestEvent) {
- const elapsed = Date.now() - this.channel.getTimestamp(requestEvent);
- return Math.max(0, VERIFICATION_REQUEST_TIMEOUT - elapsed);
+ return this.calculateEventTimeout(requestEvent);
}
return 0;
}
@@ -525,7 +545,7 @@ export class VerificationRequest extends EventEmitter {
}
const cancelEvent = this._getEventByEither(CANCEL_TYPE);
- if (cancelEvent && phase() !== PHASE_DONE) {
+ if ((this._cancelled || cancelEvent) && phase() !== PHASE_DONE) {
transitions.push({phase: PHASE_CANCELLED, event: cancelEvent});
return transitions;
}
@@ -734,7 +754,7 @@ export class VerificationRequest extends EventEmitter {
_setupTimeout(phase) {
const shouldTimeout = !this._timeoutTimer && !this.observeOnly &&
- phase === PHASE_REQUESTED && this.initiatedByMe;
+ phase === PHASE_REQUESTED;
if (shouldTimeout) {
this._timeoutTimer = setTimeout(this._cancelOnTimeout, this.timeout);
@@ -753,7 +773,17 @@ export class VerificationRequest extends EventEmitter {
_cancelOnTimeout = () => {
try {
- this.cancel({reason: "Other party didn't accept in time", code: "m.timeout"});
+ if (this.initiatedByMe) {
+ this.cancel({
+ reason: "Other party didn't accept in time",
+ code: "m.timeout",
+ });
+ } else {
+ this.cancel({
+ reason: "User didn't accept in time",
+ code: "m.timeout",
+ });
+ }
} catch (err) {
logger.error("Error while cancelling verification request", err);
}
@@ -790,16 +820,8 @@ export class VerificationRequest extends EventEmitter {
if (!isLiveEvent) {
this._observeOnly = true;
}
- // a timestamp is not provided on all to_device events
- const timestamp = this.channel.getTimestamp(event);
- if (Number.isFinite(timestamp)) {
- const elapsed = Date.now() - timestamp;
- // don't allow interaction on old requests
- if (elapsed > (VERIFICATION_REQUEST_TIMEOUT - VERIFICATION_REQUEST_MARGIN) ||
- elapsed < -(VERIFICATION_REQUEST_TIMEOUT / 2)
- ) {
- this._observeOnly = true;
- }
+ if (this.calculateEventTimeout(event) < VERIFICATION_REQUEST_MARGIN) {
+ this._observeOnly = true;
}
}
@@ -818,6 +840,8 @@ export class VerificationRequest extends EventEmitter {
this._eventsByThem.delete(type);
}
}
+ // also remember when we received the request event
+ this._requestReceivedAt = Date.now();
}
}
@@ -858,6 +882,15 @@ export class VerificationRequest extends EventEmitter {
return true;
}
+ onVerifierCancelled() {
+ this._cancelled = true;
+ // move to cancelled phase
+ const newTransitions = this._applyPhaseTransitions();
+ if (newTransitions.length) {
+ this._setPhase(newTransitions[newTransitions.length - 1].phase);
+ }
+ }
+
onVerifierFinished() {
this.channel.send("m.key.verification.done", {});
this._verifierHasFinished = true;
@@ -867,4 +900,8 @@ export class VerificationRequest extends EventEmitter {
this._setPhase(newTransitions[newTransitions.length - 1].phase);
}
}
+
+ getEventFromOtherParty(type) {
+ return this._eventsByThem.get(type);
+ }
}
diff --git a/src/filter-component.js b/src/filter-component.js
index 7e8fd524..8ff76067 100644
--- a/src/filter-component.js
+++ b/src/filter-component.js
@@ -108,8 +108,9 @@ FilterComponent.prototype._checkFields =
}
const allowed_values = self[name];
- if (allowed_values) {
- if (!allowed_values.map(match_func)) {
+ if (allowed_values && allowed_values.length > 0) {
+ const anyMatch = allowed_values.some(match_func);
+ if (!anyMatch) {
return false;
}
}
diff --git a/src/filter.js b/src/filter.js
index f8884b27..6ab581c6 100644
--- a/src/filter.js
+++ b/src/filter.js
@@ -56,13 +56,6 @@ Filter.LAZY_LOADING_MESSAGES_FILTER = {
lazy_load_members: true,
};
-Filter.LAZY_LOADING_SYNC_FILTER = {
- room: {
- state: Filter.LAZY_LOADING_MESSAGES_FILTER,
- },
-};
-
-
/**
* Get the ID of this filter on your homeserver (if known)
* @return {?Number} The filter ID
@@ -96,6 +89,7 @@ Filter.prototype.setDefinition = function(definition) {
// "state": {
// "types": ["m.room.*"],
// "not_rooms": ["!726s6s6q:example.com"],
+ // "lazy_load_members": true,
// },
// "timeline": {
// "limit": 10,
@@ -177,6 +171,10 @@ Filter.prototype.setTimelineLimit = function(limit) {
setProp(this.definition, "room.timeline.limit", limit);
};
+Filter.prototype.setLazyLoadMembers = function(enabled) {
+ setProp(this.definition, "room.state.lazy_load_members", !!enabled);
+};
+
/**
* Control whether left rooms should be included in responses.
* @param {boolean} includeLeave True to make rooms the user has left appear
diff --git a/src/http-api.js b/src/http-api.js
index d297c308..e7134bb5 100644
--- a/src/http-api.js
+++ b/src/http-api.js
@@ -276,6 +276,9 @@ MatrixHttpApi.prototype = {
callbacks.clearTimeout(xhr.timeout_timer);
var resp;
try {
+ if (xhr.status === 0) {
+ throw new AbortError();
+ }
if (!xhr.responseText) {
throw new Error('No response body.');
}
@@ -789,6 +792,17 @@ const requestCallback = function(
userDefinedCallback = userDefinedCallback || function() {};
return function(err, response, body) {
+ if (err) {
+ // the unit tests use matrix-mock-request, which throw the string "aborted" when aborting a request.
+ // See https://github.com/matrix-org/matrix-mock-request/blob/3276d0263a561b5b8326b47bae720578a2c7473a/src/index.js#L48
+ const aborted = err.name === "AbortError" || err === "aborted";
+ if (!aborted && !(err instanceof MatrixError)) {
+ // browser-request just throws normal Error objects,
+ // not `TypeError`s like fetch does. So just assume any
+ // error is due to the connection.
+ err = new ConnectionError("request failed", err);
+ }
+ }
if (!err) {
try {
if (response.statusCode >= 400) {
@@ -892,12 +906,76 @@ function getResponseContentType(response) {
* @prop {Object} data The raw Matrix error JSON used to construct this object.
* @prop {integer} httpStatus The numeric HTTP status code given
*/
-export function MatrixError(errorJson) {
- errorJson = errorJson || {};
- this.errcode = errorJson.errcode;
- this.name = errorJson.errcode || "Unknown error code";
- this.message = errorJson.error || "Unknown message";
- this.data = errorJson;
+export class MatrixError extends Error {
+ constructor(errorJson) {
+ errorJson = errorJson || {};
+ super(`MatrixError: ${errorJson.errcode}`);
+ this.errcode = errorJson.errcode;
+ this.name = errorJson.errcode || "Unknown error code";
+ this.message = errorJson.error || "Unknown message";
+ this.data = errorJson;
+ }
+}
+
+/**
+ * Construct a ConnectionError. This is a JavaScript Error indicating
+ * that a request failed because of some error with the connection, either
+ * CORS was not correctly configured on the server, the server didn't response,
+ * the request timed out, or the internet connection on the client side went down.
+ * @constructor
+ */
+export class ConnectionError extends Error {
+ constructor(message, cause = undefined) {
+ super(message + (cause ? `: ${cause.message}` : ""));
+ this._cause = cause;
+ }
+
+ get name() {
+ return "ConnectionError";
+ }
+
+ get cause() {
+ return this._cause;
+ }
+}
+
+export class AbortError extends Error {
+ constructor() {
+ super("Operation aborted");
+ }
+
+ get name() {
+ return "AbortError";
+ }
+}
+
+/**
+ * Retries a network operation run in a callback.
+ * @param {number} maxAttempts maximum attempts to try
+ * @param {Function} callback callback that returns a promise of the network operation. If rejected with ConnectionError, it will be retried by calling the callback again.
+ * @return {any} the result of the network operation
+ * @throws {ConnectionError} If after maxAttempts the callback still throws ConnectionError
+ */
+export async function retryNetworkOperation(maxAttempts, callback) {
+ let attempts = 0;
+ let lastConnectionError = null;
+ while (attempts < maxAttempts) {
+ try {
+ if (attempts > 0) {
+ const timeout = 1000 * Math.pow(2, attempts);
+ console.log(`network operation failed ${attempts} times,` +
+ ` retrying in ${timeout}ms...`);
+ await new Promise(r => setTimeout(r, timeout));
+ }
+ return await callback();
+ } catch (err) {
+ if (err instanceof ConnectionError) {
+ attempts += 1;
+ lastConnectionError = err;
+ } else {
+ throw err;
+ }
+ }
+ }
+ throw lastConnectionError;
}
-MatrixError.prototype = Object.create(Error.prototype);
-MatrixError.prototype.constructor = MatrixError;
diff --git a/src/interactive-auth.js b/src/interactive-auth.js
index eb0e25a5..ad47701e 100644
--- a/src/interactive-auth.js
+++ b/src/interactive-auth.js
@@ -143,11 +143,13 @@ InteractiveAuth.prototype = {
this._resolveFunc = resolve;
this._rejectFunc = reject;
- // if we have no flows, try a request (we'll have
- // just a session ID in _data if resuming)
- if (!this._data.flows) {
+ const hasFlows = this._data && this._data.flows;
+
+ // if we have no flows, try a request to acquire the flows
+ if (!hasFlows) {
if (this._busyChangedCallback) this._busyChangedCallback(true);
- this._doRequest(this._data).finally(() => {
+ // Do a fresh request as we're just acquiring flows.
+ this._doRequest(null).finally(() => {
if (this._busyChangedCallback) this._busyChangedCallback(false);
});
} else {
@@ -186,7 +188,11 @@ InteractiveAuth.prototype = {
}
authDict = {
type: EMAIL_STAGE_TYPE,
+ // TODO: Remove `threepid_creds` once servers support proper UIA
+ // See https://github.com/matrix-org/synapse/issues/5665
+ // See https://github.com/matrix-org/matrix-doc/issues/2220
threepid_creds: creds,
+ threepidCreds: creds,
};
}
}
@@ -264,11 +270,16 @@ InteractiveAuth.prototype = {
}
}
- // use the sessionid from the last request.
- const auth = {
- session: this._data.session,
- };
- utils.extend(auth, authData);
+ // use the sessionid from the last request, if one is present.
+ let auth;
+ if (this._data.session) {
+ auth = {
+ session: this._data.session,
+ };
+ utils.extend(auth, authData);
+ } else {
+ auth = authData;
+ }
try {
// NB. the 'background' flag is deprecated by the busyChanged
@@ -325,7 +336,7 @@ InteractiveAuth.prototype = {
} catch (error) {
// sometimes UI auth errors don't come with flows
const errorFlows = error.data ? error.data.flows : null;
- const haveFlows = Boolean(this._data.flows) || Boolean(errorFlows);
+ const haveFlows = this._data.flows || Boolean(errorFlows);
if (error.httpStatus !== 401 || !error.data || !haveFlows) {
// doesn't look like an interactive-auth failure.
if (!background) {
@@ -351,7 +362,13 @@ InteractiveAuth.prototype = {
error.data.session = this._data.session;
}
this._data = error.data;
- this._startNextAuthStage();
+ try {
+ this._startNextAuthStage();
+ } catch (e) {
+ this._rejectFunc(e);
+ this._resolveFunc = null;
+ this._rejectFunc = null;
+ }
if (
!this._emailSid &&
@@ -414,7 +431,7 @@ InteractiveAuth.prototype = {
return;
}
- if (this._data.errcode || this._data.error) {
+ if (this._data && this._data.errcode || this._data.error) {
this._stateUpdatedCallback(nextStage, {
errcode: this._data.errcode || "",
error: this._data.error || "",
diff --git a/src/matrix.js b/src/matrix.ts
index f9f71c54..7a60404d 100644
--- a/src/matrix.js
+++ b/src/matrix.ts
@@ -16,8 +16,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+import type Request from "request";
+
import {MemoryCryptoStore} from "./crypto/store/memory-crypto-store";
+import {LocalStorageCryptoStore} from "./crypto/store/localStorage-crypto-store";
+import {IndexedDBCryptoStore} from "./crypto/store/indexeddb-crypto-store";
import {MemoryStore} from "./store/memory";
+import {StubStore} from "./store/stub";
+import {LocalIndexedDBStoreBackend} from "./store/indexeddb-local-backend";
+import {RemoteIndexedDBStoreBackend} from "./store/indexeddb-remote-backend";
import {MatrixScheduler} from "./scheduler";
import {MatrixClient} from "./client";
@@ -89,6 +96,10 @@ export function wrapRequest(wrapper) {
};
}
+type Store =
+ StubStore | MemoryStore | LocalIndexedDBStoreBackend | RemoteIndexedDBStoreBackend;
+
+type CryptoStore = MemoryCryptoStore | LocalStorageCryptoStore | IndexedDBCryptoStore;
let cryptoStoreFactory = () => new MemoryCryptoStore;
@@ -102,6 +113,15 @@ export function setCryptoStoreFactory(fac) {
cryptoStoreFactory = fac;
}
+
+interface ICreateClientOpts {
+ baseUrl: string;
+ store?: Store;
+ cryptoStore?: CryptoStore;
+ scheduler?: MatrixScheduler;
+ request?: Request;
+}
+
/**
* Construct a Matrix Client. Similar to {@link module:client.MatrixClient}
* except that the 'request', 'store' and 'scheduler' dependencies are satisfied.
@@ -125,10 +145,10 @@ export function setCryptoStoreFactory(fac) {
* @see {@link module:client.MatrixClient} for the full list of options for
* <code>opts</code>.
*/
-export function createClient(opts) {
+export function createClient(opts: ICreateClientOpts | string) {
if (typeof opts === "string") {
opts = {
- "baseUrl": opts,
+ "baseUrl": opts as string,
};
}
opts.request = opts.request || requestInstance;
diff --git a/src/models/event.js b/src/models/event.js
index 5b110cbc..90365432 100644
--- a/src/models/event.js
+++ b/src/models/event.js
@@ -390,11 +390,12 @@ utils.extend(MatrixEvent.prototype, {
* @internal
*
* @param {module:crypto} crypto crypto module
+ * @param {bool} isRetry True if this is a retry (enables more logging)
*
* @returns {Promise} promise which resolves (to undefined) when the decryption
* attempt is completed.
*/
- attemptDecryption: async function(crypto) {
+ attemptDecryption: async function(crypto, isRetry) {
// start with a couple of sanity checks.
if (!this.isEncrypted()) {
throw new Error("Attempt to decrypt event which isn't encrypted");
@@ -406,7 +407,7 @@ utils.extend(MatrixEvent.prototype, {
) {
// we may want to just ignore this? let's start with rejecting it.
throw new Error(
- "Attempt to decrypt event which has already been encrypted",
+ "Attempt to decrypt event which has already been decrypted",
);
}
@@ -424,7 +425,7 @@ utils.extend(MatrixEvent.prototype, {
return this._decryptionPromise;
}
- this._decryptionPromise = this._decryptionLoop(crypto);
+ this._decryptionPromise = this._decryptionLoop(crypto, isRetry);
return this._decryptionPromise;
},
@@ -469,7 +470,7 @@ utils.extend(MatrixEvent.prototype, {
return recipients;
},
- _decryptionLoop: async function(crypto) {
+ _decryptionLoop: async function(crypto, isRetry) {
// make sure that this method never runs completely synchronously.
// (doing so would mean that we would clear _decryptionPromise *before*
// it is set in attemptDecryption - and hence end up with a stuck
@@ -486,13 +487,18 @@ utils.extend(MatrixEvent.prototype, {
res = this._badEncryptedMessage("Encryption not enabled");
} else {
res = await crypto.decryptEvent(this);
+ if (isRetry) {
+ logger.info(`Decrypted event on retry (id=${this.getId()})`);
+ }
}
} catch (e) {
if (e.name !== "DecryptionError") {
// not a decryption error: log the whole exception as an error
// (and don't bother with a retry)
+ const re = isRetry ? 're' : '';
logger.error(
- `Error decrypting event (id=${this.getId()}): ${e.stack || e}`,
+ `Error ${re}decrypting event ` +
+ `(id=${this.getId()}): ${e.stack || e}`,
);
this._decryptionPromise = null;
this._retryDecryption = false;
diff --git a/src/models/room.js b/src/models/room.js
index 2381ce15..abed7252 100644
--- a/src/models/room.js
+++ b/src/models/room.js
@@ -209,7 +209,7 @@ utils.inherits(Room, EventEmitter);
Room.prototype.getVersion = function() {
const createEvent = this.currentState.getStateEvents("m.room.create", "");
if (!createEvent) {
- logger.warn("Room " + this.room_id + " does not have an m.room.create event");
+ logger.warn("Room " + this.roomId + " does not have an m.room.create event");
return '1';
}
const ver = createEvent.getContent()['room_version'];
@@ -675,7 +675,7 @@ Room.prototype.hasUnverifiedDevices = async function() {
}
const e2eMembers = await this.getEncryptionTargetMembers();
for (const member of e2eMembers) {
- const devices = await this._client.getStoredDevicesForUser(member.userId);
+ const devices = this._client.getStoredDevicesForUser(member.userId);
if (devices.some((device) => device.isUnverified())) {
return true;
}
diff --git a/src/pushprocessor.js b/src/pushprocessor.js
index 4c733841..da7fb04a 100644
--- a/src/pushprocessor.js
+++ b/src/pushprocessor.js
@@ -23,9 +23,8 @@ import {escapeRegExp, globToRegexp, isNullOrUndefined} from "./utils";
const RULEKINDS_IN_ORDER = ['override', 'content', 'room', 'sender', 'underride'];
-// The default override rules to apply when calculating actions for an event. These
-// defaults apply under no other circumstances to avoid confusing the client with server
-// state. We do this for two reasons:
+// The default override rules to apply to the push rules that arrive from the server.
+// We do this for two reasons:
// 1. Synapse is unlikely to send us the push rule in an incremental sync - see
// https://github.com/matrix-org/synapse/pull/4867#issuecomment-481446072 for
// more details.
@@ -364,33 +363,6 @@ export function PushProcessor(client) {
return actionObj;
};
- const applyRuleDefaults = function(clientRuleset) {
- // Deep clone the object before we mutate it
- const ruleset = JSON.parse(JSON.stringify(clientRuleset));
-
- if (!clientRuleset['global']) {
- clientRuleset['global'] = {};
- }
- if (!clientRuleset['global']['override']) {
- clientRuleset['global']['override'] = [];
- }
-
- // Apply default overrides
- const globalOverrides = clientRuleset['global']['override'];
- for (const override of DEFAULT_OVERRIDE_RULES) {
- const existingRule = globalOverrides
- .find((r) => r.rule_id === override.rule_id);
-
- if (!existingRule) {
- const ruleId = override.rule_id;
- console.warn(`Adding default global override for ${ruleId}`);
- globalOverrides.push(override);
- }
- }
-
- return ruleset;
- };
-
this.ruleMatchesEvent = function(rule, ev) {
let ret = true;
for (let i = 0; i < rule.conditions.length; ++i) {
@@ -410,8 +382,7 @@ export function PushProcessor(client) {
* @return {PushAction}
*/
this.actionsForEvent = function(ev) {
- const rules = applyRuleDefaults(client.pushRules);
- return pushActionsForEventAndRulesets(ev, rules);
+ return pushActionsForEventAndRulesets(ev, client.pushRules);
};
/**
@@ -476,18 +447,25 @@ PushProcessor.rewriteDefaultRules = function(incomingRules) {
if (!newRules.global) newRules.global = {};
if (!newRules.global.override) newRules.global.override = [];
- // Fix default override rules
- newRules.global.override = newRules.global.override.map(r => {
- const defaultRule = DEFAULT_OVERRIDE_RULES.find(d => d.rule_id === r.rule_id);
- if (!defaultRule) return r;
-
- // Copy over the actions, default, and conditions. Don't touch the user's
- // preference.
- r.default = defaultRule.default;
- r.conditions = defaultRule.conditions;
- r.actions = defaultRule.actions;
- return r;
- });
+ // Merge the client-level defaults with the ones from the server
+ const globalOverrides = newRules.global.override;
+ for (const override of DEFAULT_OVERRIDE_RULES) {
+ const existingRule = globalOverrides
+ .find((r) => r.rule_id === override.rule_id);
+
+ if (existingRule) {
+ // Copy over the actions, default, and conditions. Don't touch the user's
+ // preference.
+ existingRule.default = override.default;
+ existingRule.conditions = override.conditions;
+ existingRule.actions = override.actions;
+ } else {
+ // Add the rule
+ const ruleId = override.rule_id;
+ console.warn(`Adding default global override for ${ruleId}`);
+ globalOverrides.push(override);
+ }
+ }
return newRules;
};
diff --git a/src/store/memory.js b/src/store/memory.js
index e420e05a..bf2e9cfd 100644
--- a/src/store/memory.js
+++ b/src/store/memory.js
@@ -25,6 +25,15 @@ limitations under the License.
import {User} from "../models/user";
import * as utils from "../utils";
+function isValidFilterId(filterId) {
+ const isValidStr = typeof filterId === "string" &&
+ !!filterId &&
+ filterId !== "undefined" && // exclude these as we've serialized undefined in localStorage before
+ filterId !== "null";
+
+ return isValidStr || typeof filterId === "number";
+}
+
/**
* Construct a new in-memory data store for the Matrix Client.
* @constructor
@@ -273,8 +282,17 @@ MemoryStore.prototype = {
if (!this.localStorage) {
return null;
}
+ const key = "mxjssdk_memory_filter_" + filterName;
+ // XXX Storage.getItem doesn't throw ...
+ // or are we using something different
+ // than window.localStorage in some cases
+ // that does throw?
+ // that would be very naughty
try {
- return this.localStorage.getItem("mxjssdk_memory_filter_" + filterName);
+ const value = this.localStorage.getItem(key);
+ if (isValidFilterId(value)) {
+ return value;
+ }
} catch (e) {}
return null;
},
@@ -288,8 +306,13 @@ MemoryStore.prototype = {
if (!this.localStorage) {
return;
}
+ const key = "mxjssdk_memory_filter_" + filterName;
try {
- this.localStorage.setItem("mxjssdk_memory_filter_" + filterName, filterId);
+ if (isValidFilterId(filterId)) {
+ this.localStorage.setItem(key, filterId);
+ } else {
+ this.localStorage.removeItem(key);
+ }
} catch (e) {}
},
diff --git a/src/sync.js b/src/sync.js
index 99a7f8d2..142e85fb 100644
--- a/src/sync.js
+++ b/src/sync.js
@@ -511,6 +511,12 @@ SyncApi.prototype.sync = function() {
checkLazyLoadStatus(); // advance to the next stage
}
+ function buildDefaultFilter() {
+ const filter = new Filter(client.credentials.userId);
+ filter.setTimelineLimit(self.opts.initialSyncLimit);
+ return filter;
+ }
+
const checkLazyLoadStatus = async () => {
debuglog("Checking lazy load status...");
if (this.opts.lazyLoadMembers && client.isGuest()) {
@@ -520,19 +526,11 @@ SyncApi.prototype.sync = function() {
debuglog("Checking server lazy load support...");
const supported = await client.doesServerSupportLazyLoading();
if (supported) {
- try {
- debuglog("Creating and storing lazy load sync filter...");
- this.opts.filter = await client.createFilter(
- Filter.LAZY_LOADING_SYNC_FILTER,
- );
- debuglog("Created and stored lazy load sync filter");
- } catch (err) {
- logger.error(
- "Creating and storing lazy load sync filter failed",
- err,
- );
- throw err;
+ debuglog("Enabling lazy load on sync filter...");
+ if (!this.opts.filter) {
+ this.opts.filter = buildDefaultFilter();
}
+ this.opts.filter.setLazyLoadMembers(true);
} else {
debuglog("LL: lazy loading requested but not supported " +
"by server, so disabling");
@@ -575,8 +573,7 @@ SyncApi.prototype.sync = function() {
if (self.opts.filter) {
filter = self.opts.filter;
} else {
- filter = new Filter(client.credentials.userId);
- filter.setTimelineLimit(self.opts.initialSyncLimit);
+ filter = buildDefaultFilter();
}
let filterId;
diff --git a/src/utils.ts b/src/utils.ts
index 1e31dff8..e5fd7d61 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -21,6 +21,7 @@ limitations under the License.
*/
import unhomoglyph from 'unhomoglyph';
+import {ConnectionError} from "./http-api";
/**
* Encode a dictionary of query parameters.
@@ -265,7 +266,7 @@ export function checkObjectHasNoAdditionalKeys(obj: object, allowedKeys: string[
* @param {Object} obj The object to deep copy.
* @return {Object} A copy of the object without any references to the original.
*/
-export function deepCopy(obj: object): object {
+export function deepCopy<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
@@ -372,6 +373,7 @@ export function extend() {
const target = arguments[0] || {};
for (let i = 1; i < arguments.length; i++) {
const source = arguments[i];
+ if (!source) continue;
for (const propName in source) { // eslint-disable-line guard-for-in
target[propName] = source[propName];
}
diff --git a/tsconfig.json b/tsconfig.json
index e01a31f6..548bbe7f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
+ "esModuleInterop": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2016",
diff --git a/yarn.lock b/yarn.lock
index f1405a8c..8886a93f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -25,42 +25,43 @@
dependencies:
"@babel/highlight" "^7.8.3"
-"@babel/[email protected]^7.8.6":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.8.6.tgz#7eeaa0dfa17e50c7d9c0832515eee09b56f04e35"
- integrity sha512-CurCIKPTkS25Mb8mz267vU95vy+TyUpnctEX2lV33xWNmHAfjruztgiPBbXZRh3xZZy1CYvGx6XfxyTVS+sk7Q==
+"@babel/[email protected]^7.8.6", "@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c"
+ integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==
dependencies:
- browserslist "^4.8.5"
+ browserslist "^4.9.1"
invariant "^2.2.4"
semver "^5.5.0"
"@babel/[email protected]^7.0.0", "@babel/[email protected]^7.1.0", "@babel/[email protected]^7.7.5":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.7.tgz#b69017d221ccdeb203145ae9da269d72cf102f3b"
- integrity sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e"
+ integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==
dependencies:
"@babel/code-frame" "^7.8.3"
- "@babel/generator" "^7.8.7"
- "@babel/helpers" "^7.8.4"
- "@babel/parser" "^7.8.7"
+ "@babel/generator" "^7.9.0"
+ "@babel/helper-module-transforms" "^7.9.0"
+ "@babel/helpers" "^7.9.0"
+ "@babel/parser" "^7.9.0"
"@babel/template" "^7.8.6"
- "@babel/traverse" "^7.8.6"
- "@babel/types" "^7.8.7"
+ "@babel/traverse" "^7.9.0"
+ "@babel/types" "^7.9.0"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.1"
- json5 "^2.1.0"
+ json5 "^2.1.2"
lodash "^4.17.13"
resolve "^1.3.2"
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/[email protected]^7.4.0", "@babel/[email protected]^7.8.6", "@babel/[email protected]^7.8.7":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.7.tgz#870b3cf7984f5297998152af625c4f3e341400f7"
- integrity sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==
+"@babel/[email protected]^7.4.0", "@babel/[email protected]^7.9.0", "@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9"
+ integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==
dependencies:
- "@babel/types" "^7.8.7"
+ "@babel/types" "^7.9.5"
jsesc "^2.5.1"
lodash "^4.17.13"
source-map "^0.5.0"
@@ -80,15 +81,6 @@
"@babel/helper-explode-assignable-expression" "^7.8.3"
"@babel/types" "^7.8.3"
-"@babel/[email protected]^7.8.7":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.8.7.tgz#28a279c2e6c622a6233da548127f980751324cab"
- integrity sha512-doAA5LAKhsFCR0LAFIf+r2RSMmC+m8f/oQ+URnUET/rWeEzC0yTRmAGyWkD4sSu3xwbS7MYQ2u+xlt1V5R56KQ==
- dependencies:
- "@babel/helper-hoist-variables" "^7.8.3"
- "@babel/traverse" "^7.8.3"
- "@babel/types" "^7.8.7"
-
"@babel/[email protected]^7.8.7":
version "7.8.7"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz#dac1eea159c0e4bd46e309b5a1b04a66b53c1dde"
@@ -101,25 +93,25 @@
semver "^5.5.0"
"@babel/[email protected]^7.8.3":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.6.tgz#243a5b46e2f8f0f674dc1387631eb6b28b851de0"
- integrity sha512-klTBDdsr+VFFqaDHm5rR69OpEQtO2Qv8ECxHS1mNhJJvaHArR6a1xTf5K/eZW7eZpJbhCx3NW1Yt/sKsLXLblg==
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.5.tgz#79753d44017806b481017f24b02fd4113c7106ea"
+ integrity sha512-IipaxGaQmW4TfWoXdqjY0TzoXQ1HRS0kPpEgvjosb3u7Uedcq297xFqDQiCcQtRRwzIMif+N1MLVI8C5a4/PAA==
dependencies:
- "@babel/helper-function-name" "^7.8.3"
+ "@babel/helper-function-name" "^7.9.5"
"@babel/helper-member-expression-to-functions" "^7.8.3"
"@babel/helper-optimise-call-expression" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.6"
"@babel/helper-split-export-declaration" "^7.8.3"
-"@babel/[email protected]^7.8.3":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.6.tgz#7fa040c97fb8aebe1247a5c645330c32d083066b"
- integrity sha512-bPyujWfsHhV/ztUkwGHz/RPV1T1TDEsSZDsN42JPehndA+p1KKTh3npvTadux0ZhCrytx9tvjpWNowKby3tM6A==
+"@babel/[email protected]^7.8.3", "@babel/[email protected]^7.8.8":
+ version "7.8.8"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087"
+ integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.8.3"
"@babel/helper-regex" "^7.8.3"
- regexpu-core "^4.6.0"
+ regexpu-core "^4.7.0"
"@babel/[email protected]^7.8.3":
version "7.8.3"
@@ -138,14 +130,14 @@
"@babel/traverse" "^7.8.3"
"@babel/types" "^7.8.3"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca"
- integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==
+"@babel/[email protected]^7.8.3", "@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c"
+ integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==
dependencies:
"@babel/helper-get-function-arity" "^7.8.3"
"@babel/template" "^7.8.3"
- "@babel/types" "^7.8.3"
+ "@babel/types" "^7.9.5"
"@babel/[email protected]^7.8.3":
version "7.8.3"
@@ -175,17 +167,17 @@
dependencies:
"@babel/types" "^7.8.3"
-"@babel/[email protected]^7.8.3":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.8.6.tgz#6a13b5eecadc35692047073a64e42977b97654a4"
- integrity sha512-RDnGJSR5EFBJjG3deY0NiL0K9TO8SXxS9n/MPsbPK/s9LbQymuLNtlzvDiNS7IpecuL45cMeLVkA+HfmlrnkRg==
+"@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5"
+ integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==
dependencies:
"@babel/helper-module-imports" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.6"
"@babel/helper-simple-access" "^7.8.3"
"@babel/helper-split-export-declaration" "^7.8.3"
"@babel/template" "^7.8.6"
- "@babel/types" "^7.8.6"
+ "@babel/types" "^7.9.0"
lodash "^4.17.13"
"@babel/[email protected]^7.8.3":
@@ -243,6 +235,11 @@
dependencies:
"@babel/types" "^7.8.3"
+"@babel/[email protected]^7.9.0", "@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
+ integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==
+
"@babel/[email protected]^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610"
@@ -253,28 +250,28 @@
"@babel/traverse" "^7.8.3"
"@babel/types" "^7.8.3"
-"@babel/[email protected]^7.8.4":
- version "7.8.4"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73"
- integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==
+"@babel/[email protected]^7.9.0":
+ version "7.9.2"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f"
+ integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==
dependencies:
"@babel/template" "^7.8.3"
- "@babel/traverse" "^7.8.4"
- "@babel/types" "^7.8.3"
+ "@babel/traverse" "^7.9.0"
+ "@babel/types" "^7.9.0"
"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
- integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
+ integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==
dependencies:
+ "@babel/helper-validator-identifier" "^7.9.0"
chalk "^2.0.0"
- esutils "^2.0.2"
js-tokens "^4.0.0"
-"@babel/[email protected]^7.1.0", "@babel/[email protected]^7.2.3", "@babel/[email protected]^7.4.3", "@babel/[email protected]^7.4.4", "@babel/[email protected]^7.7.0", "@babel/[email protected]^7.8.6", "@babel/[email protected]^7.8.7":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.7.tgz#7b8facf95d25fef9534aad51c4ffecde1a61e26a"
- integrity sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==
+"@babel/[email protected]^7.1.0", "@babel/[email protected]^7.2.3", "@babel/[email protected]^7.4.3", "@babel/[email protected]^7.7.0", "@babel/[email protected]^7.8.6", "@babel/[email protected]^7.9.0", "@babel/[email protected]^7.9.4":
+ version "7.9.4"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
+ integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
"@babel/[email protected]^7.8.3":
version "7.8.3"
@@ -317,7 +314,7 @@
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
-"@babel/[email protected]^7.7.4":
+"@babel/[email protected]^7.7.4", "@babel/[email protected]^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8"
integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==
@@ -325,13 +322,14 @@
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-syntax-numeric-separator" "^7.8.3"
-"@babel/[email protected]^7.7.4", "@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb"
- integrity sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA==
+"@babel/[email protected]^7.7.4", "@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz#3fd65911306d8746014ec0d0cf78f0e39a149116"
+ integrity sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+ "@babel/plugin-transform-parameters" "^7.9.5"
"@babel/[email protected]^7.8.3":
version "7.8.3"
@@ -341,20 +339,20 @@
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543"
- integrity sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg==
+"@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58"
+ integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/plugin-syntax-optional-chaining" "^7.8.0"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz#b646c3adea5f98800c9ab45105ac34d06cd4a47f"
- integrity sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==
+"@babel/[email protected]^7.4.4", "@babel/[email protected]^7.8.3":
+ version "7.8.8"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d"
+ integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==
dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+ "@babel/helper-create-regexp-features-plugin" "^7.8.8"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/[email protected]^7.8.0":
@@ -385,7 +383,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/[email protected]^7.8.3":
+"@babel/[email protected]^7.8.0", "@babel/[email protected]^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f"
integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==
@@ -458,14 +456,14 @@
"@babel/helper-plugin-utils" "^7.8.3"
lodash "^4.17.13"
-"@babel/[email protected]^7.8.6":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.6.tgz#77534447a477cbe5995ae4aee3e39fbc8090c46d"
- integrity sha512-k9r8qRay/R6v5aWZkrEclEhKO6mc1CCQr2dLsVHBmOQiMpN6I2bpjX3vgnldUWeEI1GHVNByULVxZ4BdP4Hmdg==
+"@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c"
+ integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.8.3"
"@babel/helper-define-map" "^7.8.3"
- "@babel/helper-function-name" "^7.8.3"
+ "@babel/helper-function-name" "^7.9.5"
"@babel/helper-optimise-call-expression" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.6"
@@ -479,14 +477,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz#20ddfbd9e4676906b1056ee60af88590cc7aaa0b"
- integrity sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==
+"@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz#72c97cf5f38604aea3abf3b935b0e17b1db76a50"
+ integrity sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
-"@babel/[email protected]^7.8.3":
+"@babel/[email protected]^7.4.4", "@babel/[email protected]^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e"
integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==
@@ -509,10 +507,10 @@
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
-"@babel/[email protected]^7.8.6":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz#a051bd1b402c61af97a27ff51b468321c7c2a085"
- integrity sha512-M0pw4/1/KI5WAxPsdcUL/w2LJ7o89YHN3yLkzNjg7Yl15GlVGgzHyCU+FMeAxevHGsLVmUqbirlUIKTafPmzdw==
+"@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e"
+ integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
@@ -538,41 +536,41 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz#65606d44616b50225e76f5578f33c568a0b876a5"
- integrity sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ==
+"@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz#19755ee721912cf5bb04c07d50280af3484efef4"
+ integrity sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==
dependencies:
- "@babel/helper-module-transforms" "^7.8.3"
+ "@babel/helper-module-transforms" "^7.9.0"
"@babel/helper-plugin-utils" "^7.8.3"
babel-plugin-dynamic-import-node "^2.3.0"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz#df251706ec331bd058a34bdd72613915f82928a5"
- integrity sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg==
+"@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz#e3e72f4cbc9b4a260e30be0ea59bdf5a39748940"
+ integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==
dependencies:
- "@babel/helper-module-transforms" "^7.8.3"
+ "@babel/helper-module-transforms" "^7.9.0"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/helper-simple-access" "^7.8.3"
babel-plugin-dynamic-import-node "^2.3.0"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz#d8bbf222c1dbe3661f440f2f00c16e9bb7d0d420"
- integrity sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg==
+"@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz#e9fd46a296fc91e009b64e07ddaa86d6f0edeb90"
+ integrity sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==
dependencies:
"@babel/helper-hoist-variables" "^7.8.3"
- "@babel/helper-module-transforms" "^7.8.3"
+ "@babel/helper-module-transforms" "^7.9.0"
"@babel/helper-plugin-utils" "^7.8.3"
babel-plugin-dynamic-import-node "^2.3.0"
-"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz#592d578ce06c52f5b98b02f913d653ffe972661a"
- integrity sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw==
+"@babel/[email protected]^7.9.0":
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697"
+ integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==
dependencies:
- "@babel/helper-module-transforms" "^7.8.3"
+ "@babel/helper-module-transforms" "^7.9.0"
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/[email protected]^7.8.3":
@@ -597,12 +595,11 @@
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.3"
-"@babel/[email protected]^7.8.7":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.7.tgz#66fa2f1de4129b4e0447509223ac71bda4955395"
- integrity sha512-brYWaEPTRimOctz2NDA3jnBbDi7SVN2T4wYuu0aqSzxC3nozFZngGaw29CJ9ZPweB7k+iFmZuoG3IVPIcXmD2g==
+"@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795"
+ integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==
dependencies:
- "@babel/helper-call-delegate" "^7.8.7"
"@babel/helper-get-function-arity" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
@@ -628,9 +625,9 @@
"@babel/helper-plugin-utils" "^7.8.3"
"@babel/[email protected]^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.8.3.tgz#c0153bc0a5375ebc1f1591cb7eea223adea9f169"
- integrity sha512-/vqUt5Yh+cgPZXXjmaG9NT8aVfThKk7G4OqkVhrXqwsC5soMn/qTCxs36rZ2QFhpfTJcjw4SNDIZ4RUb8OL4jQ==
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz#45468c0ae74cc13204e1d3b1f4ce6ee83258af0b"
+ integrity sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==
dependencies:
"@babel/helper-module-imports" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
@@ -674,10 +671,10 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
-"@babel/[email protected]^7.8.3":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.7.tgz#48bccff331108a7b3a28c3a4adc89e036dc3efda"
- integrity sha512-7O0UsPQVNKqpHeHLpfvOG4uXmlw+MOxYvUv6Otc9uH5SYMIxvF6eBdjkWvC3f9G+VXe0RsNExyAQBeTRug/wqQ==
+"@babel/[email protected]^7.9.0":
+ version "7.9.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.4.tgz#4bb4dde4f10bbf2d787fce9707fb09b483e33359"
+ integrity sha512-yeWeUkKx2auDbSxRe8MusAG+n4m9BFY/v+lPjmQDgOFX5qnySkUY5oXzkp6FwPdsYqnKay6lorXYdC0n3bZO7w==
dependencies:
"@babel/helper-create-class-features-plugin" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
@@ -700,11 +697,11 @@
regenerator-runtime "^0.13.4"
"@babel/[email protected]^7.7.6":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.8.7.tgz#1fc7d89c7f75d2d70c2b6768de6c2e049b3cb9db"
- integrity sha512-BYftCVOdAYJk5ASsznKAUl53EMhfBbr8CJ1X+AJLfGPscQkwJFiaV/Wn9DPH/7fzm2v6iRYJKYHSqyynTGw0nw==
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.5.tgz#8ddc76039bc45b774b19e2fc548f6807d8a8919f"
+ integrity sha512-eWGYeADTlPJH+wq1F0wNfPbVS1w1wtmMJiYk55Td5Yu28AsdR9AsC97sZ0Qq8fHqQuslVSIYSGJMcblr345GfQ==
dependencies:
- "@babel/compat-data" "^7.8.6"
+ "@babel/compat-data" "^7.9.0"
"@babel/helper-compilation-targets" "^7.8.7"
"@babel/helper-module-imports" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
@@ -712,14 +709,16 @@
"@babel/plugin-proposal-dynamic-import" "^7.8.3"
"@babel/plugin-proposal-json-strings" "^7.8.3"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3"
- "@babel/plugin-proposal-object-rest-spread" "^7.8.3"
+ "@babel/plugin-proposal-numeric-separator" "^7.8.3"
+ "@babel/plugin-proposal-object-rest-spread" "^7.9.5"
"@babel/plugin-proposal-optional-catch-binding" "^7.8.3"
- "@babel/plugin-proposal-optional-chaining" "^7.8.3"
+ "@babel/plugin-proposal-optional-chaining" "^7.9.0"
"@babel/plugin-proposal-unicode-property-regex" "^7.8.3"
"@babel/plugin-syntax-async-generators" "^7.8.0"
"@babel/plugin-syntax-dynamic-import" "^7.8.0"
"@babel/plugin-syntax-json-strings" "^7.8.0"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+ "@babel/plugin-syntax-numeric-separator" "^7.8.0"
"@babel/plugin-syntax-object-rest-spread" "^7.8.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
"@babel/plugin-syntax-optional-chaining" "^7.8.0"
@@ -728,24 +727,24 @@
"@babel/plugin-transform-async-to-generator" "^7.8.3"
"@babel/plugin-transform-block-scoped-functions" "^7.8.3"
"@babel/plugin-transform-block-scoping" "^7.8.3"
- "@babel/plugin-transform-classes" "^7.8.6"
+ "@babel/plugin-transform-classes" "^7.9.5"
"@babel/plugin-transform-computed-properties" "^7.8.3"
- "@babel/plugin-transform-destructuring" "^7.8.3"
+ "@babel/plugin-transform-destructuring" "^7.9.5"
"@babel/plugin-transform-dotall-regex" "^7.8.3"
"@babel/plugin-transform-duplicate-keys" "^7.8.3"
"@babel/plugin-transform-exponentiation-operator" "^7.8.3"
- "@babel/plugin-transform-for-of" "^7.8.6"
+ "@babel/plugin-transform-for-of" "^7.9.0"
"@babel/plugin-transform-function-name" "^7.8.3"
"@babel/plugin-transform-literals" "^7.8.3"
"@babel/plugin-transform-member-expression-literals" "^7.8.3"
- "@babel/plugin-transform-modules-amd" "^7.8.3"
- "@babel/plugin-transform-modules-commonjs" "^7.8.3"
- "@babel/plugin-transform-modules-systemjs" "^7.8.3"
- "@babel/plugin-transform-modules-umd" "^7.8.3"
+ "@babel/plugin-transform-modules-amd" "^7.9.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.9.0"
+ "@babel/plugin-transform-modules-systemjs" "^7.9.0"
+ "@babel/plugin-transform-modules-umd" "^7.9.0"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3"
"@babel/plugin-transform-new-target" "^7.8.3"
"@babel/plugin-transform-object-super" "^7.8.3"
- "@babel/plugin-transform-parameters" "^7.8.7"
+ "@babel/plugin-transform-parameters" "^7.9.5"
"@babel/plugin-transform-property-literals" "^7.8.3"
"@babel/plugin-transform-regenerator" "^7.8.7"
"@babel/plugin-transform-reserved-words" "^7.8.3"
@@ -755,25 +754,37 @@
"@babel/plugin-transform-template-literals" "^7.8.3"
"@babel/plugin-transform-typeof-symbol" "^7.8.4"
"@babel/plugin-transform-unicode-regex" "^7.8.3"
- "@babel/types" "^7.8.7"
- browserslist "^4.8.5"
+ "@babel/preset-modules" "^0.1.3"
+ "@babel/types" "^7.9.5"
+ browserslist "^4.9.1"
core-js-compat "^3.6.2"
invariant "^2.2.2"
levenary "^1.1.1"
semver "^5.5.0"
+"@babel/[email protected]^0.1.3":
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72"
+ integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==
+ 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"
+
"@babel/[email protected]^7.7.4":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.8.3.tgz#90af8690121beecd9a75d0cc26c6be39d1595d13"
- integrity sha512-qee5LgPGui9zQ0jR1TeU5/fP9L+ovoArklEqY12ek8P/wV5ZeM/VYSQYwICeoT6FfpJTekG9Ilay5PhwsOpMHA==
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz#87705a72b1f0d59df21c179f7c3d2ef4b16ce192"
+ integrity sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
- "@babel/plugin-transform-typescript" "^7.8.3"
+ "@babel/plugin-transform-typescript" "^7.9.0"
"@babel/[email protected]^7.7.4":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.8.6.tgz#a1066aa6168a73a70c35ef28cc5865ccc087ea69"
- integrity sha512-7IDO93fuRsbyml7bAafBQb3RcBGlCpU4hh5wADA2LJEEcYk92WkwFZ0pHyIi2fb5Auoz1714abETdZKCOxN0CQ==
+ version "7.9.0"
+ resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.9.0.tgz#02464ede57548bddbb5e9f705d263b7c3f43d48b"
+ integrity sha512-Tv8Zyi2J2VRR8g7pC5gTeIN8Ihultbmk0ocyNz8H2nEZbmhp1N6q0A1UGsQbDvGP/sNinQKUHf3SqXwqjtFv4Q==
dependencies:
find-cache-dir "^2.0.0"
lodash "^4.17.13"
@@ -782,9 +793,9 @@
source-map-support "^0.5.16"
"@babel/[email protected]^7.0.0", "@babel/[email protected]^7.8.3", "@babel/[email protected]^7.8.4":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d"
- integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==
+ version "7.9.2"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06"
+ integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==
dependencies:
regenerator-runtime "^0.13.4"
@@ -797,27 +808,27 @@
"@babel/parser" "^7.8.6"
"@babel/types" "^7.8.6"
-"@babel/[email protected]^7.1.0", "@babel/[email protected]^7.4.3", "@babel/[email protected]^7.7.0", "@babel/[email protected]^7.8.3", "@babel/[email protected]^7.8.4", "@babel/[email protected]^7.8.6":
- version "7.8.6"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff"
- integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==
+"@babel/[email protected]^7.1.0", "@babel/[email protected]^7.4.3", "@babel/[email protected]^7.7.0", "@babel/[email protected]^7.8.3", "@babel/[email protected]^7.8.6", "@babel/[email protected]^7.9.0":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2"
+ integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==
dependencies:
"@babel/code-frame" "^7.8.3"
- "@babel/generator" "^7.8.6"
- "@babel/helper-function-name" "^7.8.3"
+ "@babel/generator" "^7.9.5"
+ "@babel/helper-function-name" "^7.9.5"
"@babel/helper-split-export-declaration" "^7.8.3"
- "@babel/parser" "^7.8.6"
- "@babel/types" "^7.8.6"
+ "@babel/parser" "^7.9.0"
+ "@babel/types" "^7.9.5"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.13"
-"@babel/[email protected]^7.0.0", "@babel/[email protected]^7.3.0", "@babel/[email protected]^7.4.0", "@babel/[email protected]^7.7.0", "@babel/[email protected]^7.8.3", "@babel/[email protected]^7.8.6", "@babel/[email protected]^7.8.7":
- version "7.8.7"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d"
- integrity sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==
+"@babel/[email protected]^7.0.0", "@babel/[email protected]^7.3.0", "@babel/[email protected]^7.4.0", "@babel/[email protected]^7.4.4", "@babel/[email protected]^7.7.0", "@babel/[email protected]^7.8.3", "@babel/[email protected]^7.8.6", "@babel/[email protected]^7.9.0", "@babel/[email protected]^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444"
+ integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==
dependencies:
- esutils "^2.0.2"
+ "@babel/helper-validator-identifier" "^7.9.5"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
@@ -983,9 +994,9 @@
integrity sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==
"@types/[email protected]^7.1.0":
- version "7.1.6"
- resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.6.tgz#16ff42a5ae203c9af1c6e190ed1f30f83207b610"
- integrity sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg==
+ version "7.1.7"
+ resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89"
+ integrity sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==
dependencies:
"@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0"
@@ -1009,9 +1020,9 @@
"@babel/types" "^7.0.0"
"@types/[email protected]*", "@types/[email protected]^7.0.6":
- version "7.0.9"
- resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.9.tgz#be82fab304b141c3eee81a4ce3b034d0eba1590a"
- integrity sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw==
+ version "7.0.10"
+ resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.10.tgz#d9a99f017317d9b3d1abc2ced45d3bca68df0daf"
+ integrity sha512-74fNdUGrWsgIB/V9kTO5FGHPWYY6Eqn+3Z7L6Hc4e/BxjYV7puvBqp5HwsVYYfLm6iURYBNCx4Ut37OF9yitCw==
dependencies:
"@babel/types" "^7.3.0"
@@ -1022,6 +1033,11 @@
dependencies:
"@types/babel-types" "*"
+"@types/[email protected]*":
+ version "0.12.2"
+ resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
+ integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==
+
"@types/[email protected]*", "@types/[email protected]^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
@@ -1047,16 +1063,36 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
+"@types/[email protected]*":
+ version "13.13.1"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.1.tgz#1ba94c5a177a1692518bfc7b41aec0aa1a14354e"
+ integrity sha512-uysqysLJ+As9jqI5yqjwP3QJrhOcUwBjHUlUxPxjbplwKoILvXVsmYWEhfmAQlrPfbRZmhJB007o4L9sKqtHqQ==
+
- version "12.12.29"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.29.tgz#46275f028b4e463b9ac5fefc1d08bc66cc193f25"
- integrity sha512-yo8Qz0ygADGFptISDj3pOC9wXfln/5pQaN/ysDIzOaAWXt73cNHmtEC8zSO2Y+kse/txmwIAJzkYZ5fooaS5DQ==
+ version "12.12.34"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.34.tgz#0a5d6ae5d22612f0cf5f10320e1fc5d2a745dcb8"
+ integrity sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==
+
+"@types/[email protected]^2.48.4":
+ version "2.48.4"
+ resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.4.tgz#df3d43d7b9ed3550feaa1286c6eabf0738e6cf7e"
+ integrity sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==
+ dependencies:
+ "@types/caseless" "*"
+ "@types/node" "*"
+ "@types/tough-cookie" "*"
+ form-data "^2.5.0"
"@types/[email protected]^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
+"@types/[email protected]*":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"
+ integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==
+
"@types/[email protected]*":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@@ -1070,18 +1106,19 @@
"@types/yargs-parser" "*"
"@typescript-eslint/[email protected]^2.5.0":
- version "2.23.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.23.0.tgz#5d2261c8038ec1698ca4435a8da479c661dc9242"
- integrity sha512-OswxY59RcXH3NNPmq+4Kis2CYZPurRU6mG5xPcn24CjFyfdVli5mySwZz/g/xDbJXgDsYqNGq7enV0IziWGXVQ==
+ version "2.27.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.27.0.tgz#801a952c10b58e486c9a0b36cf21e2aab1e9e01a"
+ integrity sha512-vOsYzjwJlY6E0NJRXPTeCGqjv5OHgRU1kzxHKWJVPjDYGbPgLudBXjIlc+OD1hDBZ4l1DLbOc5VjofKahsu9Jw==
dependencies:
"@types/json-schema" "^7.0.3"
- "@typescript-eslint/typescript-estree" "2.23.0"
+ "@typescript-eslint/typescript-estree" "2.27.0"
eslint-scope "^5.0.0"
+ eslint-utils "^2.0.0"
-"@typescript-eslint/[email protected]":
- version "2.23.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.23.0.tgz#d355960fab96bd550855488dcc34b9a4acac8d36"
- integrity sha512-pmf7IlmvXdlEXvE/JWNNJpEvwBV59wtJqA8MLAxMKLXNKVRC3HZBXR/SlZLPWTCcwOSg9IM7GeRSV3SIerGVqw==
+"@typescript-eslint/[email protected]":
+ version "2.27.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.27.0.tgz#a288e54605412da8b81f1660b56c8b2e42966ce8"
+ integrity sha512-t2miCCJIb/FU8yArjAvxllxbTiyNqaXJag7UOpB5DVoM3+xnjeOngtqlJkLRnMtzaRcJhe3CIR9RmL40omubhg==
dependencies:
debug "^4.1.1"
eslint-visitor-keys "^1.1.0"
@@ -1497,7 +1534,7 @@ [email protected]^1.5.0:
dependencies:
file-uri-to-path "1.0.0"
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@@ -1630,16 +1667,16 @@ [email protected]~0.2.0:
pako "~1.0.5"
- version "16.5.0"
- resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.5.0.tgz#a1c2bc0431bec11fd29151941582e3f645ede881"
- integrity sha512-6bfI3cl76YLAnCZ75AGu/XPOsqUhRyc0F/olGIJeCxtfxF2HvPKEcmjU9M8oAPxl4uBY1U7Nry33Q6koV3f2iw==
+ version "16.5.1"
+ resolved "https://registry.yarnpkg.com/browserify/-/browserify-16.5.1.tgz#3c13c97436802930d5c3ae28658ddc33bfd37dc2"
+ integrity sha512-EQX0h59Pp+0GtSRb5rL6OTfrttlzv+uyaUVlK6GX3w11SQ0jKPKyjC/54RhPR2ib2KmfcELM06e8FxcI5XNU2A==
dependencies:
JSONStream "^1.0.3"
assert "^1.4.0"
browser-pack "^6.0.1"
browser-resolve "^1.11.0"
browserify-zlib "~0.2.0"
- buffer "^5.0.2"
+ buffer "~5.2.1"
cached-path-relative "^1.0.0"
concat-stream "^1.6.0"
console-browserify "^1.1.0"
@@ -1657,7 +1694,7 @@ [email protected]^16.5.0:
inherits "~2.0.1"
insert-module-globals "^7.0.0"
labeled-stream-splicer "^2.0.0"
- mkdirp "^0.5.0"
+ mkdirp-classic "^0.5.2"
module-deps "^6.0.0"
os-browserify "~0.3.0"
parents "^1.0.1"
@@ -1683,14 +1720,15 @@ [email protected]^16.5.0:
vm-browserify "^1.0.0"
xtend "^4.0.0"
- version "4.9.1"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.9.1.tgz#01ffb9ca31a1aef7678128fc6a2253316aa7287c"
- integrity sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==
+ version "4.11.1"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.1.tgz#92f855ee88d6e050e7e7311d987992014f1a1f1b"
+ integrity sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==
dependencies:
- caniuse-lite "^1.0.30001030"
- electron-to-chromium "^1.3.363"
- node-releases "^1.1.50"
+ caniuse-lite "^1.0.30001038"
+ electron-to-chromium "^1.3.390"
+ node-releases "^1.1.53"
+ pkg-up "^2.0.0"
version "4.0.1"
@@ -1716,10 +1754,10 @@ [email protected]^1.0.3:
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce"
- integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
+ integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
@@ -1769,10 +1807,10 @@ [email protected]^5.0.0, [email protected]^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
[email protected]^1.0.30001030:
- version "1.0.30001033"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001033.tgz#60c328fb56860de60f9a2cb419c31fb80587cba0"
- integrity sha512-8Ibzxee6ibc5q88cM1usPsMpJOG5CTq0s/dKOmlekPbDGKt+UrnOOTPSjQz3kVo6yL7N4SB5xd+FGLHQmbzh6A==
[email protected]^1.0.30001038:
+ version "1.0.30001039"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001039.tgz#b3814a1c38ffeb23567f8323500c09526a577bbe"
+ integrity sha512-SezbWCTT34eyFoWHgx8UWso7YtvtM7oosmFoXbCkdC6qJzRfBTeTgE9REtKtiuKXuMwWTZEvdnFNGAyVMorv8Q==
version "2.0.0"
@@ -2298,10 +2336,10 @@ [email protected]~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
- version "1.3.375"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.375.tgz#e290d59d316024e5499057944c10d05c518b7a24"
- integrity sha512-zmaFnYVBtfpF8bGRYxgPeVAlXB7N3On8rjBE2ROc6wOpTPpzRWaiHo6KkbJMvlH07CH33uks/TEb6kuMMn8q6A==
+ version "1.3.398"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.398.tgz#4c01e29091bf39e578ac3f66c1f157d92fa5725d"
+ integrity sha512-BJjxuWLKFbM5axH3vES7HKMQgAknq9PZHBkMK/rEXUQG9i1Iw5R+6hGkm6GtsQSANjSUrh/a6m32nzCNDNo/+w==
version "6.5.2"
@@ -2328,10 +2366,10 @@ [email protected]^1.1.0:
dependencies:
once "^1.4.0"
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
- integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
+ integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
version "1.3.2"
@@ -2340,10 +2378,10 @@ [email protected]^1.2.0, [email protected]^1.3.1:
dependencies:
is-arrayish "^0.2.1"
- version "1.17.4"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184"
- integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==
+ version "1.17.5"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
+ integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
@@ -2448,6 +2486,13 @@ [email protected]^1.3.1:
dependencies:
eslint-visitor-keys "^1.1.0"
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd"
+ integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==
+ dependencies:
+ eslint-visitor-keys "^1.1.0"
+
version "1.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
@@ -2510,11 +2555,11 @@ [email protected]^4.0.0, [email protected]^4.0.1, [email protected]~4.0.0:
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.1.0.tgz#c5c0b66f383e7656404f86b31334d72524eddb48"
- integrity sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe"
+ integrity sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q==
dependencies:
- estraverse "^4.0.0"
+ estraverse "^5.0.0"
version "4.2.1"
@@ -2523,11 +2568,16 @@ [email protected]^4.1.0:
dependencies:
estraverse "^4.1.0"
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22"
+ integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==
+
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@@ -2743,6 +2793,13 @@ [email protected]^2.0.0:
make-dir "^2.0.0"
pkg-dir "^3.0.0"
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
+ dependencies:
+ locate-path "^2.0.0"
+
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
@@ -2760,9 +2817,9 @@ [email protected]^2.0.1:
write "1.0.3"
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
- integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138"
+ integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==
version "1.0.2"
@@ -2774,6 +2831,15 @@ [email protected]~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
+ integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.6"
+ mime-types "^2.1.12"
+
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -2801,9 +2867,9 @@ [email protected]^1.0.0:
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.11.tgz#67bf57f4758f02ede88fb2a1712fef4d15358be3"
- integrity sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==
+ version "1.2.12"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c"
+ integrity sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==
dependencies:
bindings "^1.5.0"
nan "^2.12.1"
@@ -3001,9 +3067,9 @@ [email protected]^1.0.2:
whatwg-encoding "^1.0.1"
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491"
- integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
+ integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
version "1.1.1"
@@ -3856,7 +3922,7 @@ [email protected]^3.13.0, [email protected]^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
version "4.0.1"
resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-4.0.1.tgz#670ef71bc5661f089cc90481b99a05a1227ae3bd"
integrity sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==
@@ -3869,24 +3935,24 @@ [email protected]~0.1.0:
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
- version "3.6.3"
- resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.6.3.tgz#dccea97d0e62d63d306b8b3ed1527173b5e2190d"
- integrity sha512-Yf1ZKA3r9nvtMWHO1kEuMZTlHOF8uoQ0vyo5eH7SQy5YeIiHM+B0DgKnn+X6y6KDYZcF7G2SPkKF+JORCXWE/A==
+ version "3.6.4"
+ resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.6.4.tgz#246b2832a0ea8b37a441b61745509cfe29e174b6"
+ integrity sha512-3G9d37VHv7MFdheviDCjUfQoIjdv4TC5zTTf5G9VODLtOnVS6La1eoYBDlbWfsRT3/Xo+j2MIqki2EV12BZfwA==
dependencies:
- "@babel/parser" "^7.4.4"
- bluebird "^3.5.4"
+ "@babel/parser" "^7.9.4"
+ bluebird "^3.7.2"
catharsis "^0.8.11"
escape-string-regexp "^2.0.0"
- js2xmlparser "^4.0.0"
+ js2xmlparser "^4.0.1"
klaw "^3.0.0"
- markdown-it "^8.4.2"
- markdown-it-anchor "^5.0.2"
- marked "^0.7.0"
- mkdirp "^0.5.1"
+ markdown-it "^10.0.0"
+ markdown-it-anchor "^5.2.7"
+ marked "^0.8.2"
+ mkdirp "^1.0.4"
requizzle "^0.2.3"
- strip-json-comments "^3.0.1"
+ strip-json-comments "^3.1.0"
taffydb "2.6.2"
- underscore "~1.9.1"
+ underscore "~1.10.2"
version "11.12.0"
@@ -3962,12 +4028,12 @@ [email protected]~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6"
- integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
+ integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
dependencies:
- minimist "^1.2.0"
+ minimist "^1.2.5"
version "0.0.0"
@@ -4088,6 +4154,14 @@ [email protected]^4.0.0:
pify "^3.0.0"
strip-bom "^3.0.0"
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
+ dependencies:
+ p-locate "^2.0.0"
+ path-exists "^3.0.0"
+
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -4173,26 +4247,26 @@ [email protected]^1.0.0:
dependencies:
object-visit "^1.0.0"
- version "5.2.5"
- resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz#dbf13cfcdbffd16a510984f1263e1d479a47d27a"
- integrity sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ==
+ version "5.2.7"
+ resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.2.7.tgz#ec740f6bd03258a582cd0c65b9644b9f9852e5a3"
+ integrity sha512-REFmIaSS6szaD1bye80DMbp7ePwsPNvLTR5HunsUcZ0SG0rWJQ+Pz24R4UlTKtjKBPhxo0v0tOBDYjZQQknW8Q==
- version "8.4.2"
- resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54"
- integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc"
+ integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==
dependencies:
argparse "^1.0.7"
- entities "~1.1.1"
+ entities "~2.0.0"
linkify-it "^2.0.0"
mdurl "^1.0.1"
uc.micro "^1.0.5"
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e"
- integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355"
+ integrity sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==
version "1.2.3"
@@ -4287,15 +4361,10 @@ [email protected]:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.5.tgz#d7aa327bcecf518f9106ac6b8f003fa3bcea8566"
integrity sha1-16oye87PUY+RBqxrjwA/o7zqhWY=
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
- integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f"
- integrity sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw==
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
version "1.3.2"
@@ -4305,12 +4374,22 @@ [email protected]^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
- integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz#54c441ce4c96cd7790e10b41a87aa51068ecab2b"
+ integrity sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==
+
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
+ integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
- minimist "0.0.8"
+ minimist "^1.2.5"
+
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
version "6.2.2"
@@ -4416,12 +4495,10 @@ [email protected]^5.4.2:
shellwords "^0.1.1"
which "^1.3.0"
- version "1.1.51"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.51.tgz#70d0e054221343d2966006bfbd4d98622cc00bd0"
- integrity sha512-1eQEs6HFYY1kMXQPOLzCf7HdjReErmvn85tZESMczdCNVWP3Y7URYLBAyYynuI7yef1zj4HN5q+oB2x67QU0lw==
- dependencies:
- semver "^6.3.0"
+ version "1.1.53"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4"
+ integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==
version "2.5.0"
@@ -4580,13 +4657,27 @@ [email protected]^1.0.0:
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
+ integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
+ dependencies:
+ p-try "^1.0.0"
+
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
- integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
+ dependencies:
+ p-limit "^1.1.0"
+
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
@@ -4599,6 +4690,11 @@ [email protected]^1.0.0:
resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+ integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
@@ -4747,6 +4843,13 @@ [email protected]^3.0.0:
dependencies:
find-up "^3.0.0"
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
+ integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
+ dependencies:
+ find-up "^2.1.0"
+
version "1.1.0"
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
@@ -4800,9 +4903,9 @@ [email protected]^7.0.1:
asap "~2.0.3"
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.1.tgz#b63a9ce2809f106fa9ae1277c275b167af46ea05"
- integrity sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA==
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068"
+ integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==
dependencies:
kleur "^3.0.3"
sisteransi "^1.0.4"
@@ -4822,9 +4925,9 @@ [email protected]^1.0.2:
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
- integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
+ integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
version "4.0.3"
@@ -4967,9 +5070,9 @@ [email protected]^2.1.0, [email protected]^2.1.1:
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
- version "6.9.1"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
- integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
+ version "6.9.3"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e"
+ integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==
version "6.5.2"
@@ -5032,9 +5135,9 @@ [email protected]^4.1.1:
integrity sha512-NfJp90AvYA1R6+uSYafQ+n+UM2HjHqi4WGHeprVXa6quU9d8o6ZFRzQ36uemY82dlkZFzf2jigFx6E4UzNFajA==
- version "16.13.0"
- resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527"
- integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
version "2.0.0"
@@ -5118,10 +5221,10 @@ [email protected]^0.17.3:
private "^0.1.8"
source-map "~0.6.1"
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
- integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
+ integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==
dependencies:
regenerate "^1.4.0"
@@ -5136,14 +5239,14 @@ [email protected]^0.11.0:
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
- version "0.13.4"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz#e96bf612a3362d12bb69f7e8f74ffeab25c7ac91"
- integrity sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==
+ version "0.13.5"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
+ integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
- version "0.14.2"
- resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.2.tgz#949d9d87468ff88d5a7e4734ebb994a892de1ff2"
- integrity sha512-V4+lGplCM/ikqi5/mkkpJ06e9Bujq1NFmNLvsCs56zg3ZbzrnUzAtizZ24TXxtRX/W2jcdScwQCnbL0CICTFkQ==
+ version "0.14.4"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7"
+ integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==
dependencies:
"@babel/runtime" "^7.8.4"
private "^0.1.8"
@@ -5161,24 +5264,24 @@ [email protected]^2.0.1:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6"
- integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938"
+ integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==
dependencies:
regenerate "^1.4.0"
- regenerate-unicode-properties "^8.1.0"
- regjsgen "^0.5.0"
- regjsparser "^0.6.0"
+ regenerate-unicode-properties "^8.2.0"
+ regjsgen "^0.5.1"
+ regjsparser "^0.6.4"
unicode-match-property-ecmascript "^1.0.4"
- unicode-match-property-value-ecmascript "^1.1.0"
+ unicode-match-property-value-ecmascript "^1.2.0"
version "0.5.1"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"
integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==
version "0.6.4"
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272"
integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==
@@ -5355,9 +5458,9 @@ [email protected]^2.2.0:
is-promise "^2.1.0"
- version "6.5.4"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
- integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
+ version "6.5.5"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
+ integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==
dependencies:
tslib "^1.9.0"
@@ -5484,9 +5587,9 @@ [email protected]^0.1.1:
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
- integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
+ integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
version "1.0.0"
@@ -5494,9 +5597,9 @@ [email protected]^1.0.0:
integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3"
- integrity sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
+ integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
version "2.0.0"
@@ -5706,21 +5809,39 @@ [email protected]^3.0.0, [email protected]^3.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz#ee497fd29768646d84be2c9b819e292439614373"
+ integrity sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.5"
+
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
- integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc"
+ integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==
dependencies:
define-properties "^1.1.3"
- function-bind "^1.1.1"
+ es-abstract "^1.17.5"
+ string.prototype.trimstart "^1.0.0"
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9"
- integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3"
+ integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==
dependencies:
define-properties "^1.1.3"
- function-bind "^1.1.1"
+ es-abstract "^1.17.5"
+ string.prototype.trimend "^1.0.0"
+
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz#afe596a7ce9de905496919406c9734845f01a2f2"
+ integrity sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.5"
version "1.3.0"
@@ -5772,10 +5893,10 @@ [email protected]^2.0.0, [email protected]^2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7"
- integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180"
+ integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==
version "1.0.0"
@@ -5826,9 +5947,9 @@ [email protected]:
integrity sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=
- version "4.6.6"
- resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.6.tgz#da2382e6cafbdf86205e82fb9a115bd664d54863"
- integrity sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==
+ version "4.6.10"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.10.tgz#90f5bd069ff456ddbc9503b18e52f9c493d3b7c2"
+ integrity sha512-qbF/3UOo11Hggsbsqm2hPa6+L4w7bkr+09FNseEe8xrcVD3APGLFqE+Oz1ZKAxjYnFsj80rLOfgAtJ0LNJjtTA==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
@@ -6100,10 +6221,10 @@ [email protected]^1.1.2:
simple-concat "^1.0.0"
xtend "^4.0.1"
- version "1.9.2"
- resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.2.tgz#0c8d6f536d6f378a5af264a72f7bec50feb7cf2f"
- integrity sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf"
+ integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==
version "1.0.5"
@@ -6123,15 +6244,15 @@ [email protected]^1.0.4:
unicode-canonical-property-names-ecmascript "^1.0.4"
unicode-property-aliases-ecmascript "^1.0.4"
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277"
- integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531"
+ integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57"
- integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
+ integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
version "1.0.1"
@@ -6440,18 +6561,18 @@ [email protected]^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
- version "13.1.1"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
- integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
+ version "13.1.2"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
+ integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
- version "13.3.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"
- integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==
+ version "13.3.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
+ integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
dependencies:
cliui "^5.0.0"
find-up "^3.0.0"
@@ -6462,7 +6583,7 @@ [email protected]^13.3.0:
string-width "^3.0.0"
which-module "^2.0.0"
y18n "^4.0.0"
- yargs-parser "^13.1.1"
+ yargs-parser "^13.1.2"
version "3.10.0"