Compare commits

...

66 Commits

Author SHA1 Message Date
RiotRobot 8b6b16067b v8.2.0-rc.1 2020-08-26 11:45:48 +01:00
RiotRobot a2da0de17d Prepare changelog for v8.2.0-rc.1 2020-08-26 11:45:48 +01:00
J. Ryan Stinnett 93ff3edb6b Merge pull request #1449 from matrix-org/jryans/strict-enc-check
Add state event check
2020-08-26 11:03:42 +01:00
J. Ryan Stinnett 7c67fd69dd Add state event check
State events are never encrypted, so we can ignore them here.
2020-08-26 10:55:03 +01:00
J. Ryan Stinnett ed978f69fb Merge pull request #1444 from matrix-org/jryans/secure-backup-required
Add method to check whether client .well-known has been fetched
2020-08-24 17:32:49 +01:00
J. Ryan Stinnett 743f2465ea Document WellKnown.client event 2020-08-24 15:56:14 +01:00
J. Ryan Stinnett 41fffa233a Switch to promise-based API 2020-08-24 15:56:14 +01:00
J. Ryan Stinnett e45377166b Merge pull request #1443 from matrix-org/jryans/cross-signing-auth-errors
Handle auth errors during cross-signing key upload
2020-08-24 11:52:40 +01:00
David Baker 24939bf0b0 Merge pull request #1448 from matrix-org/dbkr/dont_fail_if_audio_output_unavailable
Don't fail if the requested audio output isn't available
2020-08-21 16:19:20 +01:00
David Baker 3221be4855 Don't fail if the requested audio output isn't available
This thorws an exception if the requested device isn't available,
in which case we should catch it  & carry on with the default device.

Fixes https://github.com/vector-im/element-web/issues/15019
2020-08-21 16:02:08 +01:00
David Baker 3135f1ed24 Merge pull request #1447 from matrix-org/dbkr/fix_log_fail
Fix logging failures
2020-08-21 11:12:11 +01:00
David Baker 1b0834ffb0 Fix logging failures 2020-08-21 11:01:34 +01:00
David Baker d79d613cb7 Merge pull request #1446 from matrix-org/dbkr/log_user_media_constraints
Log the constraints we pass to getUserMedia
2020-08-19 18:30:39 +01:00
David Baker d8cc1f7b7a Log the constraints we pass to getUserMedia
To help debug voip calls
2020-08-19 18:13:44 +01:00
J. Ryan Stinnett d7c8856fdd Add method to check whether client .well-known has been fetched
This allows clearly detecting whether we have _ever_ fetched .well-known at all.
Without this, it's hard to be sure whether the value is `undefined` because the
fetch has not been attempted yet or because an error occurred.
2020-08-19 16:10:10 +01:00
J. Ryan Stinnett 9d80a332aa Upload cross-signing keys first to handle failure
This changes to uploading cross-signing keys first, since they require a valid
UI auth session, and so are more likely to fail than other API calls. With the
new ordering, if they do fail, then by failing first, we won't have made any
changes to the user's account, so everything rolls back correctly.
2020-08-19 11:55:09 +01:00
J. Ryan Stinnett e14f7b63c7 Handle auth errors during cross-signing key upload
In order to handle auth errors (such as incorrect passwords), we need to ensure
we only try to upload cross-signing keys from within the auth flow helper
function.

This rearranges things to store that function in the builder to use it when the
actual upload happens.
2020-08-19 11:44:41 +01:00
Travis Ralston 3bd2880923 Revert "Merge pull request #1440 from matrix-org/travis/spec-i18n"
This reverts commit 2401ad7159.
2020-08-18 13:07:31 -06:00
Travis Ralston 2401ad7159 Merge pull request #1440 from matrix-org/travis/spec-i18n
Use SAS emoji data from matrix-doc
2020-08-18 11:41:36 -06:00
Travis Ralston 5d95398621 Use SAS emoji data from matrix-doc
Fixes https://github.com/vector-im/element-web/issues/14947

Much like element-web's Jitsi wrapper build steps, this downloads the emoji JSON at build time to ensure it gets reasonably updated. In the future, the spec might want to consider publishing a dedicated i18n package on npm for this, however this is fine for now. We download rather than copy/paste to ensure we always have an updated copy.
2020-08-17 15:35:03 -06:00
RiotRobot 64cdd73b93 v8.1.0 2020-08-17 12:43:32 +01:00
RiotRobot 48a9236ea8 Prepare changelog for v8.1.0 2020-08-17 12:43:31 +01:00
RiotRobot 8b3126e9d8 v8.1.0-rc.1 2020-08-13 12:05:35 +01:00
RiotRobot 74d497cd2d Prepare changelog for v8.1.0-rc.1 2020-08-13 12:05:35 +01:00
Michael Telatynski 5070a5c598 Merge pull request #1438 from brettz9/patch-1
Update on Promises
2020-08-09 13:11:51 +01:00
Brett Zamir 2e30b08e74 Update on Promises
It seems that with ES6 Promises being apparently used, the reference to `done()` is outdated.

Also clarifies that it is "problematic" to discard the results of promises rather than it being an "error" .
2020-08-09 17:39:53 +08:00
J. Ryan Stinnett 8bf63f5f0b Merge pull request #1437 from matrix-org/jryans/defer-cross-signing-setup
Store and request master cross-signing key
2020-08-07 16:06:28 +01:00
J. Ryan Stinnett 11665d18ee Cache all cross-signing private keys when checking trust
This ensures we try to get all private keys when e.g. logging in and using the
passphrase to unlock 4S.

Part of https://github.com/vector-im/element-web/issues/13896
2020-08-06 16:43:56 +01:00
J. Ryan Stinnett a8a9fc0c9d Log secret name received via sharing 2020-08-06 16:43:56 +01:00
J. Ryan Stinnett 098cd1b8d4 Request master cross-signing private key during verification
This change adds a request for the master cross-signing private key, in case the
other device is willing to share it.

Part of https://github.com/vector-im/element-web/issues/13896
2020-08-06 16:43:56 +01:00
J. Ryan Stinnett 3166a4880d Move cross-signing key request path out of verification
This was always quite awkwardly placed in verification, so this moves it to
somewhere more expected. No changes are made to the logic or features in this
commit.
2020-08-06 16:39:56 +01:00
J. Ryan Stinnett 9d1c7136cc Store master cross-signing private key in local cache
We now want to store all private keys in the local cache, including the master
key if available.

Part of https://github.com/vector-im/element-web/issues/13896
2020-08-06 16:39:56 +01:00
J. Ryan Stinnett e100943edf Fix typo in comments 2020-08-06 16:24:49 +01:00
RiotRobot a6fe4cdf1c Merge branch 'master' into develop 2020-08-05 15:30:42 +01:00
RiotRobot 8b5213c09a v8.0.1 2020-08-05 15:27:22 +01:00
RiotRobot 23a133c825 Prepare changelog for v8.0.1 2020-08-05 15:27:22 +01:00
J. Ryan Stinnett 69c4496dfe Merge pull request #1433 from matrix-org/bwindels/fixinvaliddisplaynames
Filter out non-string display names
2020-08-05 15:03:53 +01:00
Bruno Windels 4d74cca206 Merge pull request #1434 from matrix-org/bwindels/fixinvaliddisplaynames-release
Filter out non-string display names
2020-08-04 13:55:40 +00:00
Bruno Windels 955e081699 respect signature 2020-08-04 15:48:48 +02:00
Bruno Windels b7e73422ab filter out non-string display names 2020-08-04 15:48:48 +02:00
Bruno Windels a9a4ba33aa respect signature 2020-08-04 15:43:57 +02:00
Bruno Windels 7116ad9f58 filter out non-string display names 2020-08-04 15:30:32 +02:00
J. Ryan Stinnett 3d18bdb2aa Merge pull request #1427 from matrix-org/dependabot/npm_and_yarn/elliptic-6.5.3
Bump elliptic from 6.5.2 to 6.5.3
2020-08-04 10:38:10 +01:00
J. Ryan Stinnett 4c14581606 Merge pull request #1431 from matrix-org/jryans/riot-to-element
Replace Riot with Element in docs and comments
2020-08-04 10:27:32 +01:00
J. Ryan Stinnett a9c9ec3977 Replace Riot with Element in docs and comments
This only covers the simple cases of references to issues and repos. More
complex areas, such as deployment scripts, will be handled separately.

Part of https://github.com/vector-im/element-web/issues/14864
2020-08-03 18:32:52 +01:00
J. Ryan Stinnett 694d1f9631 Merge pull request #1430 from matrix-org/jryans/rm-tslint
Remove leftover bits of TSLint
2020-07-31 20:30:44 +01:00
J. Ryan Stinnett 2b9cfae18a Remove leftover bits of TSLint 2020-07-31 14:31:19 +01:00
RiotRobot 800d8380ce v8.0.1-rc.1 2020-07-31 13:18:04 +01:00
RiotRobot 621ca28f68 Prepare changelog for v8.0.1-rc.1 2020-07-31 13:18:03 +01:00
J. Ryan Stinnett 4792241ff6 Merge pull request #1426 from matrix-org/jryans/clean-lint-deps
Remove redundant lint dependencies
2020-07-31 11:42:21 +01:00
David Baker 0b984df5f9 Merge pull request #1428 from matrix-org/dbkr/upload_keys_when_new_key_backup_seen
Upload all keys when we start using a new key backup version
2020-07-30 22:46:39 +01:00
David Baker 2e260155ea Merge pull request #1429 from matrix-org/dbkr/countSessionsNeedingBackup
Expose countSessionsNeedingBackup
2020-07-30 22:46:22 +01:00
David Baker b15c8a2d1c Expose countSessionsNeedingBackup
Useful to see the number of keys waiting for backup (it's exposed
via events but you couldn;t get it directly). Also clarify doc on
the return value of `flagAllGroupSessionsForBackup` which was not
technically incorrect...
2020-07-30 19:05:45 +01:00
David Baker 94ab317f23 Upload all keys when we start using a new key backup version
Although see comment and other bug created

Fixes https://github.com/vector-im/riot-web/issues/14828
2020-07-30 18:08:48 +01:00
dependabot[bot] 02b37e1219 Bump elliptic from 6.5.2 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-30 12:41:01 +00:00
J. Ryan Stinnett 9d25848a21 Remove unused Jest lint plugin 2020-07-29 11:55:50 +01:00
J. Ryan Stinnett 3958768e1f Remove redundant lint dependencies
These are no longer needed with the new standard lint repo.
2020-07-29 11:36:57 +01:00
Jorik Schellekens cec00cd303 Merge pull request #1422 from matrix-org/joriks/conigure-eslint
Configure and use new eslint package
2020-07-28 19:53:54 +01:00
Jorik Schellekens 45cfef4294 New lines for let statements 2020-07-28 14:31:21 +01:00
Jorik Schellekens 2a01e99635 Merge branch 'develop' of github.com:matrix-org/matrix-js-sdk into joriks/conigure-eslint 2020-07-28 14:17:02 +01:00
Jorik Schellekens a9c3aee447 Revert quotes change in ts 2020-07-28 14:16:46 +01:00
Jorik Schellekens 8a974172ab Fix babel 2020-07-21 10:40:02 +01:00
Jorik Schellekens 72675f7266 Lint ts 2020-07-21 10:33:16 +01:00
Jorik Schellekens 0f559050d8 Fix whitespace issues 2020-07-21 10:00:16 +01:00
Jorik Schellekens 58084774bf fix lock 2020-07-21 09:56:17 +01:00
Jorik Schellekens c68d135ae8 Use new eslint 2020-07-20 21:48:28 +01:00
41 changed files with 1354 additions and 685 deletions
+21 -68
View File
@@ -1,71 +1,15 @@
module.exports = {
parser: "babel-eslint", // now needed for class properties
parserOptions: {
sourceType: "module",
ecmaFeatures: {
}
},
extends: ["matrix-org"],
plugins: [
"babel",
],
env: {
browser: true,
node: true,
// babel's transform-runtime converts references to ES6 globals such as
// Promise and Map to core-js polyfills, so we can use ES6 globals.
es6: true,
jest: true,
},
extends: ["eslint:recommended", "google"],
plugins: [
"babel",
"jest",
],
rules: {
// rules we've always adhered to or now do
"max-len": ["error", {
code: 90,
ignoreComments: true,
}],
curly: ["error", "multi-line"],
"prefer-const": ["error"],
"comma-dangle": ["error", {
arrays: "always-multiline",
objects: "always-multiline",
imports: "always-multiline",
exports: "always-multiline",
functions: "always-multiline",
}],
// loosen jsdoc requirements a little
"require-jsdoc": ["error", {
require: {
FunctionDeclaration: false,
}
}],
"valid-jsdoc": ["error", {
requireParamDescription: false,
requireReturn: false,
requireReturnDescription: false,
}],
// rules we do not want from eslint-recommended
"no-console": ["off"],
"no-constant-condition": ["off"],
"no-empty": ["error", { "allowEmptyCatch": true }],
// rules we do not want from the google styleguide
"object-curly-spacing": ["off"],
"spaced-comment": ["off"],
"guard-for-in": ["off"],
// in principle we prefer single quotes, but life is too short
quotes: ["off"],
// rules we'd ideally like to adhere to, but the current
// code does not (in most cases because it's still ES5)
// we set these to warnings, and assert that the number
// of warnings doesn't exceed a given threshold
"no-var": ["warn"],
"brace-style": ["warn", "1tbs", {"allowSingleLine": true}],
"prefer-rest-params": ["warn"],
"prefer-spread": ["warn"],
"one-var": ["warn"],
@@ -79,10 +23,19 @@ module.exports = {
"asyncArrow": "always",
}],
"arrow-parens": "off",
// eslint's built in no-invalid-this rule breaks with class properties
"no-invalid-this": "off",
// so we replace it with a version that is class property aware
"babel/no-invalid-this": "error",
}
}
"prefer-promise-reject-errors": "off",
"quotes": "off",
"indent": "off",
"no-constant-condition": "off",
"no-async-promise-executor": "off",
},
overrides: [{
"files": ["src/**/*.{ts, tsx}"],
"extends": ["matrix-org/ts"],
"rules": {
// While we're converting to ts we make heavy use of this
"@typescript-eslint/no-explicit-any": "off",
"quotes": "off",
},
}],
};
+62
View File
@@ -1,3 +1,65 @@
Changes in [8.2.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.2.0-rc.1) (2020-08-26)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.1.0...v8.2.0-rc.1)
* Add state event check
[\#1449](https://github.com/matrix-org/matrix-js-sdk/pull/1449)
* Add method to check whether client .well-known has been fetched
[\#1444](https://github.com/matrix-org/matrix-js-sdk/pull/1444)
* Handle auth errors during cross-signing key upload
[\#1443](https://github.com/matrix-org/matrix-js-sdk/pull/1443)
* Don't fail if the requested audio output isn't available
[\#1448](https://github.com/matrix-org/matrix-js-sdk/pull/1448)
* Fix logging failures
[\#1447](https://github.com/matrix-org/matrix-js-sdk/pull/1447)
* Log the constraints we pass to getUserMedia
[\#1446](https://github.com/matrix-org/matrix-js-sdk/pull/1446)
* Use SAS emoji data from matrix-doc
[\#1440](https://github.com/matrix-org/matrix-js-sdk/pull/1440)
Changes in [8.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.1.0) (2020-08-17)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.1.0-rc.1...v8.1.0)
* No changes since rc.1
Changes in [8.1.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.1.0-rc.1) (2020-08-13)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.0.1...v8.1.0-rc.1)
* Update on Promises
[\#1438](https://github.com/matrix-org/matrix-js-sdk/pull/1438)
* Store and request master cross-signing key
[\#1437](https://github.com/matrix-org/matrix-js-sdk/pull/1437)
* Filter out non-string display names
[\#1433](https://github.com/matrix-org/matrix-js-sdk/pull/1433)
* Bump elliptic from 6.5.2 to 6.5.3
[\#1427](https://github.com/matrix-org/matrix-js-sdk/pull/1427)
* Replace Riot with Element in docs and comments
[\#1431](https://github.com/matrix-org/matrix-js-sdk/pull/1431)
* Remove leftover bits of TSLint
[\#1430](https://github.com/matrix-org/matrix-js-sdk/pull/1430)
Changes in [8.0.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.0.1) (2020-08-05)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.0.1-rc.1...v8.0.1)
* Filter out non-string display names
[\#1434](https://github.com/matrix-org/matrix-js-sdk/pull/1434)
Changes in [8.0.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.0.1-rc.1) (2020-07-31)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.0.0...v8.0.1-rc.1)
* Remove redundant lint dependencies
[\#1426](https://github.com/matrix-org/matrix-js-sdk/pull/1426)
* Upload all keys when we start using a new key backup version
[\#1428](https://github.com/matrix-org/matrix-js-sdk/pull/1428)
* Expose countSessionsNeedingBackup
[\#1429](https://github.com/matrix-org/matrix-js-sdk/pull/1429)
* Configure and use new eslint package
[\#1422](https://github.com/matrix-org/matrix-js-sdk/pull/1422)
Changes in [8.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.0.0) (2020-07-27)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v7.1.0...v8.0.0)
+2 -4
View File
@@ -182,10 +182,8 @@ you can pass the result of the promise into it with something like:
matrixClient.someMethod(arg1, arg2).nodeify(callback);
```
The main thing to note is that it is an error to discard the result of a
promise-returning function, as that will cause exceptions to go unobserved. If
you have nothing better to do with the result, just call ``.done()`` on it. See
http://documentup.com/kriskowal/q/#the-end for more information.
The main thing to note is that it is problematic to discard the result of a
promise-returning function, as that will cause exceptions to go unobserved.
Methods which return a promise show this in their documentation.
+5 -8
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "8.0.0",
"version": "8.2.0-rc.1",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
@@ -13,10 +13,9 @@
"build:compile-browser": "mkdirp dist && browserify -d src/browser-index.js -p [ tsify -p ./tsconfig.json ] -t [ babelify --sourceMaps=inline --presets [ @babel/preset-env @babel/preset-typescript ] ] | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js",
"build:minify-browser": "terser dist/browser-matrix.js --compress --mangle --source-map --output dist/browser-matrix.min.js",
"gendoc": "jsdoc -c jsdoc.json -P package.json",
"lint": "yarn lint:types && yarn lint:ts && yarn lint:js",
"lint:js": "eslint --max-warnings 81 src spec",
"lint": "yarn lint:types && yarn lint:js",
"lint:js": "eslint --max-warnings 76 src spec",
"lint:types": "tsc --noEmit",
"lint:ts": "tslint --project ./tsconfig.json -t stylish",
"test": "jest spec/ --coverage --testEnvironment node",
"test:watch": "jest spec/ --coverage --testEnvironment node --watch"
},
@@ -75,10 +74,9 @@
"babelify": "^10.0.0",
"better-docs": "^1.4.7",
"browserify": "^16.5.0",
"eslint": "^5.12.0",
"eslint-config-google": "^0.7.1",
"eslint": "7.3.1",
"eslint-config-matrix-org": "^0.1.2",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-jest": "^23.0.4",
"exorcist": "^1.0.1",
"fake-indexeddb": "^3.0.0",
"jest": "^24.9.0",
@@ -89,7 +87,6 @@
"rimraf": "^3.0.0",
"terser": "^4.4.3",
"tsify": "^4.0.1",
"tslint": "^5.20.1",
"typescript": "^3.7.3"
},
"jest": {
+1 -1
View File
@@ -10,7 +10,7 @@
# npm; typically installed by Node.js
# yarn; install via brew (macOS) or similar (https://yarnpkg.com/docs/install/)
#
# Note: this script is also used to release matrix-react-sdk and riot-web.
# Note: this script is also used to release matrix-react-sdk and element-web.
set -e
+2 -2
View File
@@ -140,7 +140,7 @@ describe("DeviceList management:", function() {
it("We should not get confused by out-of-order device query responses",
() => {
// https://github.com/vector-im/riot-web/issues/3126
// https://github.com/vector-im/element-web/issues/3126
aliceTestClient.expectKeyQuery({device_keys: {'@alice:localhost': {}}});
return aliceTestClient.start().then(() => {
aliceTestClient.httpBackend.when('GET', '/sync').respond(
@@ -271,7 +271,7 @@ describe("DeviceList management:", function() {
});
}).timeout(3000);
// https://github.com/vector-im/riot-web/issues/4983
// https://github.com/vector-im/element-web/issues/4983
describe("Alice should know she has stale device lists", () => {
beforeEach(async function() {
await aliceTestClient.start();
+1 -1
View File
@@ -90,7 +90,7 @@ describe("MatrixClient retrying", function() {
// wait for the localecho of ev1 to be updated
const p3 = new Promise((resolve, reject) => {
room.on("Room.localEchoUpdated", (ev0) => {
if(ev0 === ev1) {
if (ev0 === ev1) {
resolve();
}
});
+2 -2
View File
@@ -346,7 +346,7 @@ describe("megolm", function() {
});
it("Alice receives a megolm message before the session keys", function() {
// https://github.com/vector-im/riot-web/issues/2273
// https://github.com/vector-im/element-web/issues/2273
let roomKeyEncrypted;
return aliceTestClient.start().then(() => {
@@ -726,7 +726,7 @@ describe("megolm", function() {
});
});
// https://github.com/vector-im/riot-web/issues/2676
// https://github.com/vector-im/element-web/issues/2676
it("Alice should send to her other devices", function() {
// for this test, we make the testOlmAccount be another of Alice's devices.
// it ought to get included in messages Alice sends.
+1 -1
View File
@@ -38,7 +38,7 @@ const testKey = new Uint8Array([
]);
const types = [
{ type: "master", shouldCache: false },
{ type: "master", shouldCache: true },
{ type: "self_signing", shouldCache: true },
{ type: "user_signing", shouldCache: true },
{ type: "invalid", shouldCache: false },
+53
View File
@@ -21,6 +21,7 @@ import * as olmlib from "../../../src/crypto/olmlib";
import {TestClient} from '../../TestClient';
import {HttpResponse, setHttpResponses} from '../../test-utils';
import {resetCrossSigningKeys, createSecretStorageKey} from "./crypto-utils";
import { MatrixError } from '../../../src/http-api';
async function makeTestClient(userInfo, options, keys) {
if (!keys) keys = {};
@@ -77,6 +78,58 @@ describe("Cross Signing", function() {
expect(alice.uploadDeviceSigningKeys).toHaveBeenCalled();
});
it("should abort bootstrap if device signing auth fails", async function() {
const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"},
);
alice.uploadDeviceSigningKeys = async (auth, keys) => {
const errorResponse = {
session: "sessionId",
flows: [
{
stages: [
"m.login.password",
],
},
],
params: {},
};
// If we're not just polling for flows, add on error rejecting the
// auth attempt.
if (auth) {
Object.assign(errorResponse, {
completed: [],
error: "Invalid password",
errcode: "M_FORBIDDEN",
});
}
const error = new MatrixError(errorResponse);
error.httpStatus == 401;
throw error;
};
alice.uploadKeySignatures = async () => {};
alice.setAccountData = async () => {};
alice.getAccountDataFromServer = async () => { };
const authUploadDeviceSigningKeys = async func => await func({});
// Try bootstrap, expecting `authUploadDeviceSigningKeys` to pass
// through failure, stopping before actually applying changes.
let bootstrapDidThrow = false;
try {
await alice.bootstrapSecretStorage({
createSecretStorageKey,
authUploadDeviceSigningKeys,
});
} catch (e) {
if (e.errcode === "M_FORBIDDEN") {
bootstrapDidThrow = true;
}
}
expect(bootstrapDidThrow).toBeTruthy();
});
it("should upload a signature when a user is verified", async function() {
const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"},
@@ -54,6 +54,7 @@ describe("self-verifications", () => {
cacheCallbacks,
);
_crossSigningInfo.keys = {
master: { keys: { X: testKeyPub } },
self_signing: { keys: { X: testKeyPub } },
user_signing: { keys: { X: testKeyPub } },
};
@@ -96,9 +97,9 @@ describe("self-verifications", () => {
const result = await verification.done();
/* We should request, and store, two cross signing key and the key backup key */
expect(cacheCallbacks.storeCrossSigningKeyCache.mock.calls.length).toBe(2);
expect(_secretStorage.request.mock.calls.length).toBe(3);
/* We should request, and store, 3 cross signing keys and the key backup key */
expect(cacheCallbacks.storeCrossSigningKeyCache.mock.calls.length).toBe(3);
expect(_secretStorage.request.mock.calls.length).toBe(4);
expect(cacheCallbacks.storeCrossSigningKeyCache.mock.calls[0][1])
.toEqual(testKey);
+1 -1
View File
@@ -1373,7 +1373,7 @@ describe("Room", function() {
let hasThrown = false;
try {
await room.loadMembersIfNeeded();
} catch(err) {
} catch (err) {
hasThrown = true;
}
expect(hasThrown).toEqual(true);
+1 -1
View File
@@ -143,7 +143,7 @@ describe("MatrixScheduler", function() {
deferA.reject({});
try {
await globalA;
} catch(err) {
} catch (err) {
await Promise.resolve();
expect(procCount).toEqual(2);
}
+1 -1
View File
@@ -66,7 +66,7 @@ function termsUrlForService(serviceType, baseUrl) {
* callback that returns a Promise<String> of an identity access token to supply
* with identity requests. If the object is unset, no access token will be
* supplied.
* See also https://github.com/vector-im/riot-web/issues/10615 which seeks to
* See also https://github.com/vector-im/element-web/issues/10615 which seeks to
* replace the previous approach of manual access tokens params with this
* callback throughout the SDK.
*
+1 -1
View File
@@ -34,7 +34,7 @@ matrixcs.request(function(opts, fn) {
let indexedDB;
try {
indexedDB = global.indexedDB;
} catch(e) {}
} catch (e) {}
// if our browser (appears to) support indexeddb, use an indexeddb crypto store.
if (indexedDB) {
+31 -14
View File
@@ -119,7 +119,7 @@ function keyFromRecoverySession(session, decryptionKey) {
* callback that returns a Promise<String> of an identity access token to supply
* with identity requests. If the object is unset, no access token will be
* supplied.
* See also https://github.com/vector-im/riot-web/issues/10615 which seeks to
* See also https://github.com/vector-im/element-web/issues/10615 which seeks to
* replace the previous approach of manual access tokens params with this
* callback throughout the SDK.
*
@@ -357,6 +357,9 @@ export function MatrixClient(opts) {
this._cachedCapabilities = null; // { capabilities: {}, lastUpdated: timestamp }
this._clientWellKnown = undefined;
this._clientWellKnownPromise = undefined;
// The SDK doesn't really provide a clean way for events to recalculate the push
// actions for themselves, so we have to kinda help them out when they are encrypted.
// We do this so that push rules are correctly executed on events in their decrypted
@@ -380,7 +383,7 @@ export function MatrixClient(opts) {
? !!actions.tweaks.highlight : false;
if (oldHighlight !== newHighlight || currentCount > 0) {
// TODO: Handle mentions received while the client is offline
// See also https://github.com/vector-im/riot-web/issues/9069
// See also https://github.com/vector-im/element-web/issues/9069
if (!room.hasUserReadEvent(this.getUserId(), event.getId())) {
let newCount = currentCount;
if (newHighlight && !oldHighlight) newCount++;
@@ -398,7 +401,7 @@ export function MatrixClient(opts) {
// Like above, we have to listen for read receipts from ourselves in order to
// correctly handle notification counts on encrypted rooms.
// This fixes https://github.com/vector-im/riot-web/issues/9421
// This fixes https://github.com/vector-im/element-web/issues/9421
this.on("Room.receipt", (event, room) => {
if (room && this.isRoomEncrypted(room.roomId)) {
// Figure out if we've read something or if it's just informational
@@ -1169,6 +1172,7 @@ wrapCryptoFuncs(MatrixClient, [
"isCrossSigningReady",
"getCryptoTrustCrossSignedDevices",
"setCryptoTrustCrossSignedDevices",
"countSessionsNeedingBackup",
]);
/**
@@ -1925,7 +1929,7 @@ MatrixClient.prototype._restoreKeyBackup = function(
let backupPubKey;
try {
backupPubKey = decryption.init_with_private_key(privKey);
} catch(e) {
} catch (e) {
decryption.free();
throw e;
}
@@ -3801,7 +3805,9 @@ MatrixClient.prototype.paginateEventTimeline = function(eventTimeline, opts) {
return pendingRequest;
}
let path, params, promise;
let path;
let params;
let promise;
const self = this;
if (isNotifTimeline) {
@@ -4221,7 +4227,8 @@ MatrixClient.prototype.getRoomPushRule = function(scope, roomId) {
*/
MatrixClient.prototype.setRoomMutePushRule = function(scope, roomId, mute) {
const self = this;
let deferred, hasDontNotifyRule;
let deferred;
let hasDontNotifyRule;
// Get the existing room-kind push rule if any
const roomPushRule = this.getRoomPushRule(scope, roomId);
@@ -4806,20 +4813,23 @@ MatrixClient.prototype.startClient = async function(opts) {
};
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);
}
// `getRawClientConfig` does not throw or reject on network errors, instead
// it absorbs errors and returns `{}`.
this._clientWellKnownPromise = AutoDiscovery.getRawClientConfig(
this.getDomain(),
);
this._clientWellKnown = await this._clientWellKnownPromise;
this.emit("WellKnown.client", this._clientWellKnown);
};
MatrixClient.prototype.getClientWellKnown = function() {
return this._clientWellKnown;
};
MatrixClient.prototype.waitForClientWellKnown = function() {
return this._clientWellKnownPromise;
};
/**
* store client options with boolean/string/numeric values
* to know in the next session what flags the sync data was
@@ -5694,6 +5704,13 @@ MatrixClient.prototype.generateClientSecret = function() {
* @param {string} data.request_id The ID of the original request.
*/
/**
* Fires when the client .well-known info is fetched.
*
* @event module:client~MatrixClient#"WellKnown.client"
* @param {object} data The JSON object returned by the server
*/
// EventEmitter JSDocs
/**
+96 -1
View File
@@ -26,6 +26,8 @@ import {logger} from '../logger';
import {IndexedDBCryptoStore} from '../crypto/store/indexeddb-crypto-store';
import {decryptAES, encryptAES} from './aes';
const KEY_REQUEST_TIMEOUT_MS = 1000 * 60;
function publicKeyFromKeyInfo(keyInfo) {
// `keys` is an object with { [`ed25519:${pubKey}`]: pubKey }
// We assume only a single key, and we want the bare form without type
@@ -72,7 +74,7 @@ export class CrossSigningInfo extends EventEmitter {
* @returns {Array} An array with [ public key, Olm.PkSigning ]
*/
async getCrossSigningKey(type, expectedPubkey) {
const shouldCache = ["self_signing", "user_signing"].indexOf(type) >= 0;
const shouldCache = ["master", "self_signing", "user_signing"].indexOf(type) >= 0;
if (!this._callbacks.getCrossSigningKey) {
throw new Error("No getCrossSigningKey callback supplied");
@@ -677,3 +679,96 @@ export function createCryptoStoreCacheCallbacks(store, olmdevice) {
},
};
}
/**
* Request cross-signing keys from another device during verification.
*
* @param {module:base-apis~MatrixBaseApis} baseApis base Matrix API interface
* @param {string} userId The user ID being verified
* @param {string} deviceId The device ID being verified
*/
export async function requestKeysDuringVerification(baseApis, userId, deviceId) {
// If this is a self-verification, ask the other party for keys
if (baseApis.getUserId() !== userId) {
return;
}
console.log("Cross-signing: Self-verification done; requesting keys");
// This happens asynchronously, and we're not concerned about waiting for
// it. We return here in order to test.
return new Promise((resolve, reject) => {
const client = baseApis;
const original = client._crypto._crossSigningInfo;
// We already have all of the infrastructure we need to validate and
// cache cross-signing keys, so instead of replicating that, here we set
// up callbacks that request them from the other device and call
// CrossSigningInfo.getCrossSigningKey() to validate/cache
const crossSigning = new CrossSigningInfo(
original.userId,
{ getCrossSigningKey: async (type) => {
console.debug("Cross-signing: requesting secret",
type, deviceId);
const { promise } = client.requestSecret(
`m.cross_signing.${type}`, [deviceId],
);
const result = await promise;
const decoded = decodeBase64(result);
return Uint8Array.from(decoded);
} },
original._cacheCallbacks,
);
crossSigning.keys = original.keys;
// XXX: get all keys out if we get one key out
// https://github.com/vector-im/element-web/issues/12604
// then change here to reject on the timeout
// Requests can be ignored, so don't wait around forever
const timeout = new Promise((resolve, reject) => {
setTimeout(
resolve,
KEY_REQUEST_TIMEOUT_MS,
new Error("Timeout"),
);
});
// also request and cache the key backup key
const backupKeyPromise = new Promise(async resolve => {
const cachedKey = await client._crypto.getSessionBackupPrivateKey();
if (!cachedKey) {
logger.info("No cached backup key found. Requesting...");
const secretReq = client.requestSecret(
'm.megolm_backup.v1', [deviceId],
);
const base64Key = await secretReq.promise;
logger.info("Got key backup key, decoding...");
const decodedKey = decodeBase64(base64Key);
logger.info("Decoded backup key, storing...");
client._crypto.storeSessionBackupPrivateKey(
Uint8Array.from(decodedKey),
);
logger.info("Backup key stored. Starting backup restore...");
const backupInfo = await client.getKeyBackupVersion();
// no need to await for this - just let it go in the bg
client.restoreKeyBackupWithCache(
undefined, undefined, backupInfo,
).then(() => {
logger.info("Backup restored.");
});
}
resolve();
});
// We call getCrossSigningKey() for its side-effects
return Promise.race([
Promise.all([
crossSigning.getCrossSigningKey("master"),
crossSigning.getCrossSigningKey("self_signing"),
crossSigning.getCrossSigningKey("user_signing"),
backupKeyPromise,
]),
timeout,
]).then(resolve, reject);
}).catch((e) => {
console.warn("Cross-signing: failure while requesting keys:", e);
});
}
+1 -1
View File
@@ -790,7 +790,7 @@ class DeviceListUpdateSerialiser {
// yield to other things that want to execute in between users, to
// avoid wedging the CPU
// (https://github.com/vector-im/riot-web/issues/3158)
// (https://github.com/vector-im/element-web/issues/3158)
//
// of course we ought to do this in a web worker or similar, but
// this serves as an easy solution for now.
+27 -17
View File
@@ -1,3 +1,4 @@
import { logger } from "../logger";
import {MatrixEvent} from "../models/event";
import {EventEmitter} from "events";
import {createCryptoStoreCacheCallbacks} from "./CrossSigning";
@@ -17,7 +18,7 @@ import {
*/
export class EncryptionSetupBuilder {
/**
* @param {Object.<String, MatrixEvent>} accountData pre-existing account data, will only be read, not written.
* @param {Object.<String, MatrixEvent>} accountData pre-existing account data, will only be read, not written.
* @param {CryptoCallbacks} delegateCryptoCallbacks crypto callbacks to delegate to if the key isn't in cache yet
*/
constructor(accountData, delegateCryptoCallbacks) {
@@ -32,11 +33,17 @@ export class EncryptionSetupBuilder {
/**
* Adds new cross-signing public keys
* @param {Object} auth auth dictionary needed to upload the new keys
*
* @param {function} authUpload Function called to await an interactive auth
* flow when uploading device signing keys.
* Args:
* {function} A function that makes the request requiring auth. Receives
* the auth data as an object. Can be called multiple times, first with
* an empty authDict, to obtain the flows.
* @param {Object} keys the new keys
*/
addCrossSigningKeys(auth, keys) {
this._crossSigningKeys = {auth, keys};
addCrossSigningKeys(authUpload, keys) {
this._crossSigningKeys = {authUpload, keys};
}
/**
@@ -113,12 +120,12 @@ export class EncryptionSetupBuilder {
* @return {Promise}
*/
async persist(crypto) {
// store self_signing and user_signing private key in cache
// store private keys in cache
if (this._crossSigningKeys) {
const cacheCallbacks = createCryptoStoreCacheCallbacks(
crypto._cryptoStore, crypto._olmDevice);
for (const type of ["self_signing", "user_signing"]) {
// logger.log(`Cache ${type} cross-signing private key locally`);
for (const type of ["master", "self_signing", "user_signing"]) {
logger.log(`Cache ${type} cross-signing private key locally`);
const privateKey = this.crossSigningCallbacks.privateKeys.get(type);
await cacheCallbacks.storeCrossSigningKeyCache(type, privateKey);
}
@@ -164,25 +171,28 @@ export class EncryptionSetupOperation {
*/
async apply(crypto) {
const baseApis = crypto._baseApis;
// set account data
if (this._accountData) {
for (const [type, content] of this._accountData) {
await baseApis.setAccountData(type, content);
}
}
// upload cross-signing keys
if (this._crossSigningKeys) {
const keys = {};
for (const [name, key] of Object.entries(this._crossSigningKeys.keys)) {
keys[name + "_key"] = key;
}
await baseApis.uploadDeviceSigningKeys(
this._crossSigningKeys.auth,
keys,
);
// We must only call `uploadDeviceSigningKeys` from inside this auth
// helper to ensure we properly handle auth errors.
await this._crossSigningKeys.authUpload(authDict => {
return baseApis.uploadDeviceSigningKeys(authDict, keys);
});
// pass the new keys to the main instance of our own CrossSigningInfo.
crypto._crossSigningInfo.setKeys(this._crossSigningKeys.keys);
}
// set account data
if (this._accountData) {
for (const [type, content] of this._accountData) {
await baseApis.setAccountData(type, content);
}
}
// upload first cross-signing signatures with the new key
// (e.g. signing our own device)
if (this._keySignatures) {
+5
View File
@@ -373,6 +373,7 @@ export class SecretStorage extends EventEmitter {
const requestId = this._baseApis.makeTxnId();
const requestControl = this._requests[requestId] = {
name,
devices,
};
const promise = new Promise((resolve, reject) => {
@@ -536,6 +537,10 @@ export class SecretStorage extends EventEmitter {
return;
}
logger.log(
`Successfully received secret ${requestControl.name} ` +
`from ${deviceInfo.deviceId}`,
);
requestControl.resolve(content.secret);
}
}
+2 -2
View File
@@ -1015,7 +1015,7 @@ MegolmEncryption.prototype._getDevicesInRoom = async function(room) {
// with them, which means that they will have announced any new devices via
// device_lists in their /sync response. This cache should then be maintained
// using all the device_lists changes and left fields.
// See https://github.com/vector-im/riot-web/issues/2305 for details.
// See https://github.com/vector-im/element-web/issues/2305 for details.
const devices = await this._crypto.downloadKeys(roomMembers, false);
const blocked = {};
// remove any blocked devices
@@ -1109,7 +1109,7 @@ MegolmDecryption.prototype.decryptEvent = async function(event) {
//
// then, if the key turns up while decryption is in progress (and
// decryption fails), we will schedule a retry.
// (fixes https://github.com/vector-im/riot-web/issues/5001)
// (fixes https://github.com/vector-im/element-web/issues/5001)
this._addEventToPendingList(event);
let res;
+54 -18
View File
@@ -413,7 +413,7 @@ Crypto.prototype.createRecoveryKeyFromPassphrase = async function(password) {
*
* If this function returns false, bootstrapSecretStorage() can be used
* to fix things such that it returns true. That is to say, after
* bootstrapSecretStorage() completes sucessfully, this function should
* bootstrapSecretStorage() completes successfully, this function should
* return true.
*
* The cross-signing API is currently UNSTABLE and may change without notice.
@@ -453,7 +453,8 @@ Crypto.prototype.isCrossSigningReady = async function() {
* called to await an interactive auth flow when uploading device signing keys.
* Args:
* {function} A function that makes the request requiring auth. Receives the
* auth data as an object. Can be called multiple times, first with an empty authDict, to obtain the flows.
* auth data as an object. Can be called multiple times, first with an empty
* authDict, to obtain the flows.
* @param {function} [opts.createSecretStorageKey] Optional. Function
* called to await a secret storage key creation flow.
* Returns:
@@ -525,17 +526,9 @@ Crypto.prototype.bootstrapSecretStorage = async function({
// sign master key with device key
await this._signObject(crossSigningInfo.keys.master);
await authUploadDeviceSigningKeys(authDict => {
if (authDict) {
builder.addCrossSigningKeys(authDict, crossSigningInfo.keys);
return Promise.resolve();
} else {
// This callback also gets called to obtain the IUA flows,
// so do a call to obtain those if we don't have the authDict yet
// We should get called again at a later point with the authDict.
return this._baseApis.uploadDeviceSigningKeys(null, {});
}
});
// Store auth flow helper function, as we need to call it when uploading
// to ensure we handle auth errors properly.
builder.addCrossSigningKeys(authUploadDeviceSigningKeys, crossSigningInfo.keys);
// cross-sign own device
const device = this._deviceList.getStoredDevice(this._userId, this._deviceId);
@@ -1221,6 +1214,20 @@ Crypto.prototype.checkOwnCrossSigningTrust = async function() {
if (oldSelfSigningId !== newCrossSigning.getId("self_signing")) {
logger.info("Got new self-signing key", newCrossSigning.getId("self_signing"));
// Try to cache the self-signing private key as a side-effect
let signing = null;
try {
const ret = await this._crossSigningInfo.getCrossSigningKey(
"self_signing", newCrossSigning.getId("self_signing"),
);
signing = ret[1];
logger.info(
"Got matching private key from callback for new public self-signing key",
);
} finally {
if (signing) signing.free();
}
const device = this._deviceList.getStoredDevice(this._userId, this._deviceId);
const signedDevice = await this._crossSigningInfo.signDevice(
this._userId, device,
@@ -1229,6 +1236,20 @@ Crypto.prototype.checkOwnCrossSigningTrust = async function() {
}
if (oldUserSigningId !== newCrossSigning.getId("user_signing")) {
logger.info("Got new user-signing key", newCrossSigning.getId("user_signing"));
// Try to cache the user-signing private key as a side-effect
let signing = null;
try {
const ret = await this._crossSigningInfo.getCrossSigningKey(
"user_signing", newCrossSigning.getId("user_signing"),
);
signing = ret[1];
logger.info(
"Got matching private key from callback for new public user-signing key",
);
} finally {
if (signing) signing.free();
}
}
if (masterChanged) {
@@ -1399,6 +1420,12 @@ Crypto.prototype._checkAndStartKeyBackup = async function() {
);
this._baseApis.disableKeyBackup();
this._baseApis.enableKeyBackup(backupInfo);
// We're now using a new backup, so schedule all the keys we have to be
// uploaded to the new backup. This is a bit of a workaround to upload
// keys to a new backup in *most* cases, but it won't cover all cases
// because we don't remember what backup version we uploaded keys to:
// see https://github.com/vector-im/element-web/issues/14833
await this.scheduleAllGroupSessionsForBackup();
} else {
logger.log("Backup version " + backupInfo.version + " still current");
}
@@ -2374,7 +2401,7 @@ Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDevic
// after all the in-memory state (_roomEncryptors and _roomList) has been updated
// to avoid races when calling this method multiple times. Hence keep a hold of the promise.
let storeConfigPromise = null;
if(!existingConfig) {
if (!existingConfig) {
storeConfigPromise = this._roomList.setRoomEncryption(roomId, config);
}
@@ -2404,10 +2431,10 @@ Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDevic
await this.trackRoomDevices(roomId);
// TODO: this flag is only not used from MatrixClient::setRoomEncryption
// which is never used (inside riot at least)
// which is never used (inside Element at least)
// but didn't want to remove it as it technically would
// be a breaking change.
if(!this.inhibitDeviceQuery) {
if (!this.inhibitDeviceQuery) {
this._deviceList.refreshOutdatedDeviceLists();
}
} else {
@@ -2712,7 +2739,8 @@ Crypto.prototype.scheduleAllGroupSessionsForBackup = async function() {
/**
* Marks all group sessions as needing to be backed up without scheduling
* them to upload in the background.
* @returns {Promise<int>} Resolves to the number of sessions requiring a backup.
* @returns {Promise<int>} Resolves to the number of sessions now requiring a backup
* (which will be equal to the number of sessions in the store).
*/
Crypto.prototype.flagAllGroupSessionsForBackup = async function() {
await this._cryptoStore.doTxn(
@@ -2735,6 +2763,14 @@ Crypto.prototype.flagAllGroupSessionsForBackup = async function() {
return remaining;
};
/**
* Counts the number of end to end session keys that are waiting to be backed up
* @returns {Promise<int>} Resolves to the number of sessions requiring backup
*/
Crypto.prototype.countSessionsNeedingBackup = function() {
return this._cryptoStore.countSessionsNeedingBackup();
};
/**
* Perform any background tasks that can be done before a message is ready to
* send, in order to speed up sending of the message.
@@ -2969,7 +3005,7 @@ Crypto.prototype.onSyncCompleted = async function(syncData) {
// we don't start uploading one-time keys until we've caught up with
// to-device messages, to help us avoid throwing away one-time-keys that we
// are about to receive messages for
// (https://github.com/vector-im/riot-web/issues/2782).
// (https://github.com/vector-im/element-web/issues/2782).
if (!syncData.catchingUp) {
_maybeUploadOneTimeKeys(this);
this._processReceivedRoomKeyRequests();
@@ -131,7 +131,7 @@ export class Backend {
cursorReq.onsuccess = (ev) => {
const cursor = ev.target.result;
if(!cursor) {
if (!cursor) {
// no match found
callback(null);
return;
+2 -91
View File
@@ -25,8 +25,7 @@ import {EventEmitter} from 'events';
import {logger} from '../../logger';
import {DeviceInfo} from '../deviceinfo';
import {newTimeoutError} from "./Error";
import {CrossSigningInfo} from "../CrossSigning";
import {decodeBase64} from "../olmlib";
import {requestKeysDuringVerification} from "../CrossSigning";
const timeoutException = new Error("Verification timed out");
@@ -79,8 +78,6 @@ export class VerificationBase extends EventEmitter {
this._transactionTimeoutTimer = null;
}
static keyRequestTimeoutMs = 1000 * 60;
get initiatedByMe() {
// if there is no start event yet,
// we probably want to send it,
@@ -198,93 +195,7 @@ export class VerificationBase extends EventEmitter {
if (!this._done) {
this.request.onVerifierFinished();
this._resolve();
//#region Cross-signing keys request
// If this is a self-verification, ask the other party for keys
if (this._baseApis.getUserId() !== this.userId) {
return;
}
// FIXME: This is a lot of logic that isn't anything to do with verification
// and probably ought to be somewhere else.
console.log("VerificationBase.done: Self-verification done; requesting keys");
/* This happens asynchronously, and we're not concerned about
* waiting for it. We return here in order to test. */
return new Promise((resolve, reject) => {
const client = this._baseApis;
const original = client._crypto._crossSigningInfo;
/* We already have all of the infrastructure we need to validate and
* cache cross-signing keys, so instead of replicating that, here we
* set up callbacks that request them from the other device and call
* CrossSigningInfo.getCrossSigningKey() to validate/cache */
const crossSigning = new CrossSigningInfo(
original.userId,
{ getCrossSigningKey: async (type) => {
console.debug("VerificationBase.done: requesting secret",
type, this.deviceId);
const { promise } = client.requestSecret(
`m.cross_signing.${type}`, [this.deviceId],
);
const result = await promise;
const decoded = decodeBase64(result);
return Uint8Array.from(decoded);
} },
original._cacheCallbacks,
);
crossSigning.keys = original.keys;
// XXX: get all keys out if we get one key out
// https://github.com/vector-im/riot-web/issues/12604
// then change here to reject on the timeout
/* Requests can be ignored, so don't wait around forever */
const timeout = new Promise((resolve, reject) => {
setTimeout(
resolve,
VerificationBase.keyRequestTimeoutMs,
new Error("Timeout"),
);
});
// also request and cache the key backup key
const backupKeyPromise = new Promise(async resolve => {
const cachedKey = await client._crypto.getSessionBackupPrivateKey();
if (!cachedKey) {
logger.info("No cached backup key found. Requesting...");
const secretReq = client.requestSecret(
'm.megolm_backup.v1', [this.deviceId],
);
const base64Key = await secretReq.promise;
logger.info("Got key backup key, decoding...");
const decodedKey = decodeBase64(base64Key);
logger.info("Decoded backup key, storing...");
client._crypto.storeSessionBackupPrivateKey(
Uint8Array.from(decodedKey),
);
logger.info("Backup key stored. Starting backup restore...");
const backupInfo = await client.getKeyBackupVersion();
// no need to await for this - just let it go in the bg
client.restoreKeyBackupWithCache(
undefined, undefined, backupInfo,
).then(() => {
logger.info("Backup restored.");
});
}
resolve();
});
/* We call getCrossSigningKey() for its side-effects */
return Promise.race([
Promise.all([
crossSigning.getCrossSigningKey("self_signing"),
crossSigning.getCrossSigningKey("user_signing"),
backupKeyPromise,
]),
timeout,
]).then(resolve, reject);
}).catch((e) => {
console.warn("VerificationBase: failure while requesting keys:", e);
});
//#endregion
return requestKeysDuringVerification(this._baseApis, this.userId, this.deviceId);
}
}
+1 -1
View File
@@ -430,7 +430,7 @@ export class SAS extends Base {
try {
await this._sendMAC(olmSAS, macMethod);
resolve();
} catch(err) {
} catch (err) {
reject(err);
}
},
+1 -1
View File
@@ -890,7 +890,7 @@ function getResponseContentType(response) {
try {
return parseContentType(contentType);
} catch(e) {
} catch (e) {
throw new Error(`Error parsing Content-Type '${contentType}': ${e}`);
}
}
+1
View File
@@ -22,6 +22,7 @@ matrixcs.request(request);
utils.runPolyfills();
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const crypto = require('crypto');
utils.setCrypto(crypto);
} catch (err) {
+1 -1
View File
@@ -187,7 +187,7 @@ export function createClient(opts: ICreateClientOpts | string) {
* @param {requestCallback} callback The request callback.
*/
/**
/**
* The request callback interface for performing HTTP requests. This matches the
* API for the {@link https://github.com/request/request#requestoptions-callback|
* request NPM module}. The SDK will implement a callback which meets this
+2 -1
View File
@@ -647,7 +647,8 @@ EventTimelineSet.prototype.compareEventOrdering = function(eventId1, eventId2) {
if (timeline1 === timeline2) {
// both events are in the same timeline - figure out their
// relative indices
let idx1, idx2;
let idx1;
let idx2;
const events = timeline1.getEvents();
for (let idx = 0; idx < events.length &&
(idx1 === undefined || idx2 === undefined); idx++) {
+2 -2
View File
@@ -87,7 +87,7 @@ export const MatrixEvent = function(
// amount of needless string duplication. This can save moderate amounts of
// memory (~10% on a 350MB heap).
// 'membership' at the event level (rather than the content level) is a legacy
// field that Riot never otherwise looks at, but it will still take up a lot
// field that Element never otherwise looks at, but it will still take up a lot
// of space if we don't intern it.
["state_key", "type", "sender", "room_id", "membership"].forEach((prop) => {
if (!event[prop]) {
@@ -626,7 +626,7 @@ utils.extend(MatrixEvent.prototype, {
* @return {boolean} True if this event is encrypted.
*/
isEncrypted: function() {
return this.event.type === "m.room.encrypted";
return !this.isState() && this.event.type === "m.room.encrypted";
},
/**
+2 -2
View File
@@ -282,9 +282,9 @@ RoomMember.prototype.getAvatarUrl =
* @return {string} the mxc avatar url
*/
RoomMember.prototype.getMxcAvatarUrl = function() {
if(this.events.member) {
if (this.events.member) {
return this.events.member.getDirectionalContent().avatar_url;
} else if(this.user) {
} else if (this.user) {
return this.user.avatarUrl;
}
return null;
+1 -1
View File
@@ -673,7 +673,7 @@ RoomState.prototype._maySendEventOfType = function(eventType, userId, state) {
const userPowerLevel = power_levels.users && power_levels.users[userId];
if (Number.isFinite(userPowerLevel)) {
powerLevel = userPowerLevel;
} else if(Number.isFinite(power_levels.users_default)) {
} else if (Number.isFinite(power_levels.users_default)) {
powerLevel = power_levels.users_default;
}
+2 -2
View File
@@ -1900,14 +1900,14 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
// let's try to figure out who was here before
let leftNames = otherNames;
// if we didn't have heroes, try finding them in the room state
if(!leftNames.length) {
if (!leftNames.length) {
leftNames = room.currentState.getMembers().filter((m) => {
return m.userId !== userId &&
m.membership !== "invite" &&
m.membership !== "join";
}).map((m) => m.name);
}
if(leftNames.length) {
if (leftNames.length) {
return `Empty room (was ${memberNamesToRoomName(leftNames)})`;
} else {
return "Empty room";
+10 -2
View File
@@ -129,7 +129,11 @@ User.prototype.setPresenceEvent = function(event) {
*/
User.prototype.setDisplayName = function(name) {
const oldName = this.displayName;
this.displayName = name;
if (typeof name === "string") {
this.displayName = name;
} else {
this.displayName = undefined;
}
if (name !== oldName) {
this._updateModifiedTime();
}
@@ -142,7 +146,11 @@ User.prototype.setDisplayName = function(name) {
* @param {string} name The new display name.
*/
User.prototype.setRawDisplayName = function(name) {
this.rawDisplayName = name;
if (typeof name === "string") {
this.rawDisplayName = name;
} else {
this.rawDisplayName = undefined;
}
};
+2 -2
View File
@@ -185,8 +185,8 @@ function _runCallbacks() {
*/
function binarySearch(array, func) {
// min is inclusive, max exclusive.
let min = 0,
max = array.length;
let min = 0;
let max = array.length;
while (min < max) {
const mid = (min + max) >> 1;
+3 -3
View File
@@ -701,7 +701,7 @@ SyncApi.prototype._syncFromCache = async function(savedSync) {
try {
await this._processSyncResponse(syncEventData, data);
} catch(e) {
} catch (e) {
logger.error("Error processing cached sync", e.stack || e);
}
@@ -774,7 +774,7 @@ SyncApi.prototype._sync = async function(syncOptions) {
try {
await this._processSyncResponse(syncEventData, data);
} catch(e) {
} catch (e) {
// log the exception with stack if we have it, else fall back
// to the plain description
logger.error("Caught /sync error", e.stack || e);
@@ -894,7 +894,7 @@ SyncApi.prototype._onSyncError = function(err, syncOptions) {
logger.error("/sync error %s", err);
logger.error(err);
if(this._shouldAbortSync(err)) {
if (this._shouldAbortSync(err)) {
return;
}
+2 -1
View File
@@ -396,7 +396,8 @@ TimelineWindow.prototype.getEvents = function() {
// (Note that both this._start.index and this._end.index are relative
// to their respective timelines' BaseIndex).
//
let startIndex = 0, endIndex = events.length;
let startIndex = 0;
let endIndex = events.length;
if (timeline === this._start.timeline) {
startIndex = this._start.index + timeline.getBaseIndex();
}
+169 -161
View File
@@ -21,7 +21,6 @@ limitations under the License.
*/
import unhomoglyph from 'unhomoglyph';
import {ConnectionError} from "./http-api";
/**
* Encode a dictionary of query parameters.
@@ -50,7 +49,7 @@ export function encodeParams(params: Record<string, string>): string {
* @return {string} The result of replacing all template variables e.g. '/foo/baz'.
*/
export function encodeUri(pathTemplate: string,
variables: Record<string, string>): string {
variables: Record<string, string>): string {
for (const key in variables) {
if (!variables.hasOwnProperty(key)) {
continue;
@@ -86,7 +85,7 @@ export function map<T, S>(array: T[], fn: (t: T) => S): S[] {
* @return {Array} A new array with the results of the function.
*/
export function filter<T>(array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean): T[] {
fn: (t: T, i?: number, a?: T[]) => boolean): T[] {
const results: T[] = [];
for (let i = 0; i < array.length; i++) {
if (fn(array[i], i, array)) {
@@ -152,10 +151,10 @@ export function forEach<T>(array: T[], fn: (t: T, i: number) => void) {
* the given function.
*/
export function findElement<T>(
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean
) {
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean,
) {
let i;
if (reverse) {
for (i = array.length - 1; i >= 0; i--) {
@@ -183,10 +182,10 @@ export function findElement<T>(
* @return {boolean} True if an element was removed.
*/
export function removeElement<T>(
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean
) {
array: T[],
fn: (t: T, i?: number, a?: T[]) => boolean,
reverse?: boolean,
) {
let i;
let removed;
if (reverse) {
@@ -294,7 +293,7 @@ export function deepCompare(x: any, y: any): boolean {
// special-case NaN (since NaN !== NaN)
if (typeof x === 'number' && isNaN(x) && isNaN(y)) {
return true;
return true;
}
// special-case null (since typeof null == 'object', but null.constructor
@@ -369,10 +368,10 @@ export function deepCompare(x: any, y: any): boolean {
*
* @return {Object} target
*/
export function extend() {
const target = arguments[0] || {};
for (let i = 1; i < arguments.length; i++) {
const source = arguments[i];
export function extend(...restParams) {
const target = restParams[0] || {};
for (let i = 1; i < restParams.length; i++) {
const source = restParams[i];
if (!source) continue;
for (const propName in source) { // eslint-disable-line guard-for-in
target[propName] = source[propName];
@@ -390,36 +389,36 @@ export function runPolyfills() {
// SOURCE:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
if (!Array.prototype.filter) {
Array.prototype.filter = function(fun: Function/*, thisArg*/) {
if (this === void 0 || this === null) {
throw new TypeError();
}
const t = Object(this);
const len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
const res = [];
const thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (let i = 0; i < len; i++) {
if (i in t) {
const val = t[i];
// NOTE: Technically this should Object.defineProperty at
// the next index, as push can be affected by
// properties on Object.prototype and Array.prototype.
// But that method's new, and collisions should be
// rare, so use the more-compatible alternative.
if (fun.call(thisArg, val, i, t)) {
res.push(val);
// eslint-disable-next-line no-extend-native
Array.prototype.filter = function(fun: Function/*, thisArg*/, ...restProps) {
if (this === void 0 || this === null) {
throw new TypeError();
}
}
}
return res;
};
const t = Object(this);
const len = t.length >>> 0;
if (typeof fun !== 'function') {
throw new TypeError();
}
const res = [];
const thisArg = restProps ? restProps[0] : void 0;
for (let i = 0; i < len; i++) {
if (i in t) {
const val = t[i];
// NOTE: Technically this should Object.defineProperty at
// the next index, as push can be affected by
// properties on Object.prototype and Array.prototype.
// But that method's new, and collisions should be
// rare, so use the more-compatible alternative.
if (fun.call(thisArg, val, i, t)) {
res.push(val);
}
}
}
return res;
};
}
// Array.prototype.map
@@ -429,151 +428,156 @@ export function runPolyfills() {
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
let T, k;
// eslint-disable-next-line no-extend-native
Array.prototype.map = function(callback, thisArg) {
let T;
let k;
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
const O = Object(this);
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
const O = Object(this);
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
const A = new Array(len);
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
const A = new Array(len);
// 7. Let k be 0
k = 0;
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
let kValue, mappedValue;
// 8. Repeat, while k < len
while (k < len) {
let kValue;
let mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
// 9. return A
return A;
};
}
// Array.prototype.forEach
// ========================================================
// SOURCE:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
let T, k;
// eslint-disable-next-line no-extend-native
Array.prototype.forEach = function(callback, thisArg) {
let T;
let k;
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
if (this === null || this === undefined) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this| value as the
// argument.
const O = Object(this);
// 1. Let O be the result of calling ToObject passing the |this| value as the
// argument.
const O = Object(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the
// argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 2. Let lenValue be the result of calling the Get internal method of O with the
// argument "length".
// 3. Let len be ToUint32(lenValue).
const len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let k be 0
k = 0;
// 6. Let k be 0
k = 0;
// 7. Repeat, while k < len
while (k < len) {
let kValue;
// 7. Repeat, while k < len
while (k < len) {
let kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with
// argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with
// argument Pk
kValue = O[k];
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with
// argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal method of O with
// argument Pk
kValue = O[k];
// ii. Call the Call internal method of callback with T as the this value and
// argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// ii. Call the Call internal method of callback with T as the this value and
// argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// 8. return undefined
};
};
}
}
@@ -657,7 +661,10 @@ export function isNumber(value: any): boolean {
* @return {string} a string with the hidden characters removed
*/
export function removeHiddenChars(str: string): string {
return unhomoglyph(str.normalize('NFD').replace(removeHiddenCharsRegex, ''));
if (typeof str === "string") {
return unhomoglyph(str.normalize('NFD').replace(removeHiddenCharsRegex, ''));
}
return "";
}
// Regex matching bunch of unicode control characters and otherwise misleading/invisible characters.
@@ -667,6 +674,7 @@ export function removeHiddenChars(str: string): string {
// LTR/RTL and other directional formatting marks U+202A - U+202F
// Combining characters U+0300 - U+036F
// Zero width no-break space (BOM) U+FEFF
// eslint-disable-next-line no-misleading-character-class
const removeHiddenCharsRegex = /[\u2000-\u200F\u202A-\u202F\u0300-\u036f\uFEFF\s]/g;
export function escapeRegExp(string: string): string {
@@ -725,9 +733,9 @@ export function defer() {
}
export async function promiseMapSeries<T>(
promises: Promise<T>[],
fn: (t: T) => void
): Promise<void> {
promises: Promise<T>[],
fn: (t: T) => void,
): Promise<void> {
for (const o of await promises) {
await fn(await o);
}
+14 -7
View File
@@ -176,7 +176,7 @@ MatrixCall.prototype.placeScreenSharingCall =
debuglog("Got screen stream, requesting audio stream...");
const audioConstraints = _getUserMediaVideoContraints('voice');
_placeCallWithConstraints(self, audioConstraints);
} catch(err) {
} catch (err) {
self.emit("error",
callError(
MatrixCall.ERR_NO_USER_MEDIA,
@@ -403,7 +403,7 @@ MatrixCall.prototype._initWithHangup = function(event) {
* Answer a call.
*/
MatrixCall.prototype.answer = function() {
debuglog("Answering call %s of type %s", this.callId, this.type);
debuglog(`Answering call ${this.callId} of type ${this.type}`);
const self = this;
if (self._answerContent) {
@@ -412,8 +412,10 @@ MatrixCall.prototype.answer = function() {
}
if (!this.localAVStream && !this.waitForLocalAVStream) {
const constraints = _getUserMediaVideoContraints(this.type);
logger.log("Getting user media with constraints", constraints);
this.webRtc.getUserMedia(
_getUserMediaVideoContraints(this.type),
constraints,
hookCallback(self, self._maybeGotUserMediaForAnswer),
hookCallback(self, self._maybeGotUserMediaForAnswer),
);
@@ -1065,7 +1067,7 @@ const terminate = function(self, hangupParty, hangupReason, shouldEmit) {
};
const stopAllMedia = function(self) {
debuglog("stopAllMedia (stream=%s)", self.localAVStream);
debuglog(`stopAllMedia (stream=${self.localAVStream})`);
if (self.localAVStream) {
forAllTracksOnStream(self.localAVStream, function(t) {
if (t.stop) {
@@ -1127,7 +1129,11 @@ const _tryPlayRemoteAudioStream = async function(self) {
const player = self.getRemoteAudioElement();
// if audioOutput is non-default:
if (audioOutput) await player.setSinkId(audioOutput);
try {
if (audioOutput) await player.setSinkId(audioOutput);
} catch (e) {
logger.warn("Couldn't set requested audio output device: using default", e);
}
player.autoplay = true;
self.assignElement(player, self.remoteAStream, "remoteAudio");
@@ -1188,8 +1194,8 @@ const _sendCandidateQueue = function(self) {
if (self.candidateSendTries > 5) {
debuglog(
"Failed to send candidates on attempt %s. Giving up for now.",
self.candidateSendTries,
"Failed to send candidates on attempt " + self.candidateSendTries +
". Giving up for now.",
);
self.candidateSendTries = 0;
return;
@@ -1205,6 +1211,7 @@ const _sendCandidateQueue = function(self) {
};
const _placeCallWithConstraints = function(self, constraints) {
logger.log("Getting user media with constraints", constraints);
self.client.callList[self.callId] = self;
self.webRtc.getUserMedia(
constraints,
-72
View File
@@ -1,72 +0,0 @@
{
"rules": {
"class-name": false,
"comment-format": [
true
],
"curly": false,
"eofline": false,
"forin": false,
"indent": [
true,
"spaces"
],
"label-position": true,
"max-line-length": false,
"member-access": false,
"member-ordering": [
true,
"static-after-instance",
"variables-before-functions"
],
"no-arg": true,
"no-bitwise": false,
"no-console": false,
"no-construct": true,
"no-debugger": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-eval": true,
"no-inferrable-types": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": false,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"quotemark": false,
"radix": true,
"semicolon": [
"always"
],
"triple-equals": [],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}
+764 -187
View File
File diff suppressed because it is too large Load Diff