Refine UISI autorageshake conditions to cut down on false alarms (#7650)

The AutoRageshakeStore now only starts submitting rageshakes after the initial sync has completed, and provides a short grace period for decryption failures to resolve.
This commit is contained in:
Faye Duxovni 2022-01-31 10:49:27 -05:00 committed by GitHub
parent f99ae6d46a
commit 7e5de9294c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -15,6 +15,8 @@ limitations under the License.
*/
import { MatrixEvent } from "matrix-js-sdk/src";
import { sleep } from "matrix-js-sdk/src/utils";
import { ISyncStateData, SyncState } from "matrix-js-sdk/src/sync";
import SdkConfig from '../SdkConfig';
import sendBugReport from '../rageshake/submit-rageshake';
@ -23,14 +25,17 @@ import { AsyncStoreWithClient } from './AsyncStoreWithClient';
import { ActionPayload } from '../dispatcher/payloads';
import SettingsStore from "../settings/SettingsStore";
// Minimum interval of 5 minutes between reports, especially important when we're doing an initial sync with a lot of decryption errors
const RAGESHAKE_INTERVAL = 5*60*1000;
// Minimum interval of 1 minute between reports
const RAGESHAKE_INTERVAL = 60000;
// Before rageshaking, wait 5 seconds and see if the message has successfully decrypted
const GRACE_PERIOD = 5000;
// Event type for to-device messages requesting sender auto-rageshakes
const AUTO_RS_REQUEST = "im.vector.auto_rs_request";
interface IState {
reportedSessionIds: Set<string>;
lastRageshakeTime: number;
initialSyncCompleted: boolean;
}
/**
@ -45,9 +50,11 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
super(defaultDispatcher, {
reportedSessionIds: new Set<string>(),
lastRageshakeTime: 0,
initialSyncCompleted: false,
});
this.onDecryptionAttempt = this.onDecryptionAttempt.bind(this);
this.onDeviceMessage = this.onDeviceMessage.bind(this);
this.onSyncStateChange = this.onSyncStateChange.bind(this);
}
public static get instance(): AutoRageshakeStore {
@ -64,6 +71,7 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
if (this.matrixClient) {
this.matrixClient.on('Event.decrypted', this.onDecryptionAttempt);
this.matrixClient.on('toDeviceEvent', this.onDeviceMessage);
this.matrixClient.on('sync', this.onSyncStateChange);
}
}
@ -75,9 +83,14 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
}
private async onDecryptionAttempt(ev: MatrixEvent): Promise<void> {
if (!this.state.initialSyncCompleted) { return; }
const wireContent = ev.getWireContent();
const sessionId = wireContent.session_id;
if (ev.isDecryptionFailure() && !this.state.reportedSessionIds.has(sessionId)) {
await sleep(GRACE_PERIOD);
if (!ev.isDecryptionFailure()) { return; }
const newReportedSessionIds = new Set(this.state.reportedSessionIds);
await this.updateState({ reportedSessionIds: newReportedSessionIds.add(sessionId) });
@ -114,6 +127,12 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
}
}
private async onSyncStateChange(_state: SyncState, _prevState: SyncState, data: ISyncStateData) {
if (!this.state.initialSyncCompleted) {
await this.updateState({ initialSyncCompleted: !!data.nextSyncToken });
}
}
private async onDeviceMessage(ev: MatrixEvent): Promise<void> {
if (ev.getType() !== AUTO_RS_REQUEST) return;
const messageContent = ev.getContent();