mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-13 00:16:17 +02:00
Remove automatic rageshakes on UTD labs feature (#32778)
* Remove automatic rageshakes on UTD feature * remove unused setting name * Remove unused lab group * Reduce count by 1
This commit is contained in:
parent
09bbf796dc
commit
772a443486
@ -18,7 +18,6 @@
|
||||
"https://scalar-staging.vector.im/api"
|
||||
],
|
||||
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
|
||||
"uisi_autorageshake_app": "element-auto-uisi",
|
||||
"show_labs_settings": false,
|
||||
"room_directory": {
|
||||
"servers": ["matrix.org", "gitter.im"]
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
"https://scalar-staging.vector.im/api"
|
||||
],
|
||||
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
|
||||
"uisi_autorageshake_app": "element-auto-uisi",
|
||||
"show_labs_settings": true,
|
||||
"room_directory": {
|
||||
"servers": ["matrix.org", "gitter.im"]
|
||||
|
||||
2
apps/web/src/@types/global.d.ts
vendored
2
apps/web/src/@types/global.d.ts
vendored
@ -39,7 +39,6 @@ import { type SetupEncryptionStore } from "../stores/SetupEncryptionStore";
|
||||
import { type RoomScrollStateStore } from "../stores/RoomScrollStateStore";
|
||||
import { type ConsoleLogger, type IndexedDBLogStore } from "../rageshake/rageshake";
|
||||
import type ActiveWidgetStore from "../stores/ActiveWidgetStore";
|
||||
import type AutoRageshakeStore from "../stores/AutoRageshakeStore";
|
||||
import { type IConfigOptions } from "../IConfigOptions";
|
||||
import { type MatrixDispatcher } from "../dispatcher/dispatcher";
|
||||
import { type DeepReadonly } from "./common";
|
||||
@ -85,7 +84,6 @@ declare global {
|
||||
matrixChat?: MatrixChat;
|
||||
mxSendSentryReport: (userText: string, issueUrl: string, error: Error) => Promise<void>;
|
||||
mxLoginWithAccessToken: (hsUrl: string, accessToken: string) => Promise<void>;
|
||||
mxAutoRageshakeStore?: AutoRageshakeStore;
|
||||
mxDispatcher: MatrixDispatcher;
|
||||
mxMatrixClientPeg: IMatrixClientPeg;
|
||||
mxReactSdkConfig: DeepReadonly<IConfigOptions>;
|
||||
|
||||
@ -108,7 +108,6 @@ export interface IConfigOptions {
|
||||
* Bug report endpoint URL. "local" means the logs should not be uploaded.
|
||||
*/
|
||||
bug_report_endpoint_url?: typeof BugReportEndpointURLLocal | string; // omission disables bug reporting
|
||||
uisi_autorageshake_app?: string; // defaults to "element-auto-uisi"
|
||||
sentry?: {
|
||||
dsn: string;
|
||||
environment?: string; // "production", etc
|
||||
|
||||
@ -22,7 +22,6 @@ export const DEFAULTS: DeepReadonly<IConfigOptions> = {
|
||||
help_key_storage_url: "https://element.io/help#encryption5",
|
||||
integrations_ui_url: "https://scalar.vector.im/",
|
||||
integrations_rest_url: "https://scalar.vector.im/api",
|
||||
uisi_autorageshake_app: "element-auto-uisi",
|
||||
show_labs_settings: false,
|
||||
force_verification: false,
|
||||
|
||||
|
||||
@ -44,7 +44,6 @@ import * as Rooms from "../../Rooms";
|
||||
import * as Lifecycle from "../../Lifecycle";
|
||||
// LifecycleStore is not used but does listen to and dispatch actions
|
||||
import "../../stores/LifecycleStore";
|
||||
import "../../stores/AutoRageshakeStore";
|
||||
import PageType from "../../PageTypes";
|
||||
import createRoom, { type IOpts } from "../../createRoom";
|
||||
import { _t, _td } from "../../languageHandler";
|
||||
|
||||
@ -75,16 +75,6 @@ export default class LabsUserSettingsTab extends React.Component<EmptyObject> {
|
||||
.getOrCreate(LabGroup.Experimental, [])
|
||||
.push(<SettingsFlag key="lowBandwidth" name="lowBandwidth" level={SettingLevel.DEVICE} />);
|
||||
|
||||
groups
|
||||
.getOrCreate(LabGroup.Analytics, [])
|
||||
.push(
|
||||
<SettingsFlag
|
||||
key="automaticDecryptionErrorReporting"
|
||||
name="automaticDecryptionErrorReporting"
|
||||
level={SettingLevel.DEVICE}
|
||||
/>,
|
||||
);
|
||||
|
||||
labsSections = (
|
||||
<>
|
||||
{sortBy(Array.from(groups.entries()), "0").map(([group, flags]) => (
|
||||
|
||||
@ -1495,7 +1495,6 @@
|
||||
},
|
||||
"labs": {
|
||||
"ask_to_join": "Enable ask to join",
|
||||
"automatic_debug_logs_decryption": "Automatically send debug logs on decryption errors",
|
||||
"beta_description": "What's next for %(brand)s? Labs are the best way to get things early, test out new features and help shape them before they actually launch.",
|
||||
"beta_feature": "This is a beta feature",
|
||||
"beta_feedback_leave_button": "To leave the beta, visit your settings.",
|
||||
|
||||
@ -89,7 +89,6 @@ export enum LabGroup {
|
||||
Threads,
|
||||
VoiceAndVideo,
|
||||
Moderation,
|
||||
Analytics,
|
||||
Themes,
|
||||
Encryption,
|
||||
Experimental,
|
||||
@ -110,7 +109,6 @@ export const labGroupNames: Record<LabGroup, TranslationKey> = {
|
||||
[LabGroup.Threads]: _td("labs|group_threads"),
|
||||
[LabGroup.VoiceAndVideo]: _td("labs|group_voip"),
|
||||
[LabGroup.Moderation]: _td("labs|group_moderation"),
|
||||
[LabGroup.Analytics]: _td("common|analytics"),
|
||||
[LabGroup.Themes]: _td("labs|group_themes"),
|
||||
[LabGroup.Encryption]: _td("labs|group_encryption"),
|
||||
[LabGroup.Experimental]: _td("labs|group_experimental"),
|
||||
@ -348,7 +346,6 @@ export interface Settings {
|
||||
"Spaces.enabledMetaSpaces": IBaseSetting<Partial<Record<MetaSpace, boolean>>>;
|
||||
"Spaces.showPeopleInSpace": IBaseSetting<boolean>;
|
||||
"developerMode": IBaseSetting<boolean>;
|
||||
"automaticDecryptionErrorReporting": IBaseSetting<boolean>;
|
||||
"debug_scroll_panel": IBaseSetting<boolean>;
|
||||
"debug_timeline_panel": IBaseSetting<boolean>;
|
||||
"debug_registration": IBaseSetting<boolean>;
|
||||
@ -1307,12 +1304,6 @@ export const SETTINGS: Settings = {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
default: false,
|
||||
},
|
||||
"automaticDecryptionErrorReporting": {
|
||||
displayName: _td("labs|automatic_debug_logs_decryption"),
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
default: false,
|
||||
controller: new ReloadOnChangeController(),
|
||||
},
|
||||
"debug_scroll_panel": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
default: false,
|
||||
|
||||
@ -1,186 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import {
|
||||
ClientEvent,
|
||||
type MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
type SyncStateData,
|
||||
type SyncState,
|
||||
ToDeviceMessageId,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import SdkConfig from "../SdkConfig";
|
||||
import sendBugReport from "../rageshake/submit-rageshake";
|
||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||
import { type ActionPayload } from "../dispatcher/payloads";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Watches for decryption errors to auto-report if the relevant lab is
|
||||
* enabled, and keeps track of session IDs that have already been
|
||||
* reported.
|
||||
*/
|
||||
export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
|
||||
private static readonly internalInstance = (() => {
|
||||
const instance = new AutoRageshakeStore();
|
||||
instance.start();
|
||||
return instance;
|
||||
})();
|
||||
|
||||
private constructor() {
|
||||
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 {
|
||||
return AutoRageshakeStore.internalInstance;
|
||||
}
|
||||
|
||||
protected async onAction(_payload: ActionPayload): Promise<void> {}
|
||||
|
||||
protected async onReady(): Promise<void> {
|
||||
if (!SettingsStore.getValue("automaticDecryptionErrorReporting")) return;
|
||||
|
||||
if (this.matrixClient) {
|
||||
this.matrixClient.on(MatrixEventEvent.Decrypted, this.onDecryptionAttempt);
|
||||
this.matrixClient.on(ClientEvent.ToDeviceEvent, this.onDeviceMessage);
|
||||
this.matrixClient.on(ClientEvent.Sync, this.onSyncStateChange);
|
||||
}
|
||||
}
|
||||
|
||||
protected async onNotReady(): Promise<void> {
|
||||
if (this.matrixClient) {
|
||||
this.matrixClient.removeListener(ClientEvent.ToDeviceEvent, this.onDeviceMessage);
|
||||
this.matrixClient.removeListener(MatrixEventEvent.Decrypted, this.onDecryptionAttempt);
|
||||
this.matrixClient.removeListener(ClientEvent.Sync, this.onSyncStateChange);
|
||||
}
|
||||
}
|
||||
|
||||
private onDecryptionAttempt = async (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) });
|
||||
|
||||
const now = new Date().getTime();
|
||||
if (now - this.state.lastRageshakeTime < RAGESHAKE_INTERVAL) {
|
||||
logger.info(
|
||||
`Not sending recipient-side autorageshake for event ${ev.getId()}/session ${sessionId}: last rageshake was too recent`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.updateState({ lastRageshakeTime: now });
|
||||
|
||||
const senderUserId = ev.getSender()!;
|
||||
const eventInfo = {
|
||||
event_id: ev.getId(),
|
||||
room_id: ev.getRoomId(),
|
||||
session_id: sessionId,
|
||||
device_id: wireContent.device_id,
|
||||
user_id: senderUserId,
|
||||
sender_key: wireContent.sender_key,
|
||||
};
|
||||
|
||||
logger.info(`Sending recipient-side autorageshake for event ${ev.getId()}/session ${sessionId}`);
|
||||
// XXX: the rageshake server returns the URL for the github issue... which is typically absent for
|
||||
// auto-uisis, because we've disabled creation of GH issues for them. So the `recipient_rageshake`
|
||||
// field is broken.
|
||||
const rageshakeURL = await sendBugReport(SdkConfig.get().bug_report_endpoint_url, {
|
||||
userText: "Auto-reporting decryption error (recipient)",
|
||||
sendLogs: true,
|
||||
labels: ["Z-UISI", "web", "uisi-recipient"],
|
||||
customApp: SdkConfig.get().uisi_autorageshake_app,
|
||||
customFields: { auto_uisi: JSON.stringify(eventInfo) },
|
||||
});
|
||||
|
||||
const messageContent = {
|
||||
...eventInfo,
|
||||
recipient_rageshake: rageshakeURL,
|
||||
[ToDeviceMessageId]: uuidv4(),
|
||||
};
|
||||
this.matrixClient?.sendToDevice(
|
||||
AUTO_RS_REQUEST,
|
||||
new Map([[senderUserId, new Map([[messageContent.device_id, messageContent]])]]),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private onSyncStateChange = async (
|
||||
_state: SyncState,
|
||||
_prevState: SyncState | null,
|
||||
data?: SyncStateData,
|
||||
): Promise<void> => {
|
||||
if (!this.state.initialSyncCompleted) {
|
||||
await this.updateState({ initialSyncCompleted: !!data?.nextSyncToken });
|
||||
}
|
||||
};
|
||||
|
||||
private onDeviceMessage = async (ev: MatrixEvent): Promise<void> => {
|
||||
if (ev.getType() !== AUTO_RS_REQUEST) return;
|
||||
const messageContent = ev.getContent();
|
||||
const recipientRageshake = messageContent["recipient_rageshake"] || "";
|
||||
const now = new Date().getTime();
|
||||
if (now - this.state.lastRageshakeTime > RAGESHAKE_INTERVAL) {
|
||||
await this.updateState({ lastRageshakeTime: now });
|
||||
logger.info(
|
||||
`Sending sender-side autorageshake for event ${messageContent["event_id"]}/session ${messageContent["session_id"]}`,
|
||||
);
|
||||
await sendBugReport(SdkConfig.get().bug_report_endpoint_url, {
|
||||
userText: `Auto-reporting decryption error (sender)\nRecipient rageshake: ${recipientRageshake}`,
|
||||
sendLogs: true,
|
||||
labels: ["Z-UISI", "web", "uisi-sender"],
|
||||
customApp: SdkConfig.get().uisi_autorageshake_app,
|
||||
customFields: {
|
||||
recipient_rageshake: recipientRageshake,
|
||||
auto_uisi: JSON.stringify(messageContent),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
logger.info(
|
||||
`Not sending sender-side autorageshake for event ${messageContent["event_id"]}/session ${messageContent["session_id"]}: last rageshake was too recent`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
window.mxAutoRageshakeStore = AutoRageshakeStore.instance;
|
||||
@ -48,6 +48,6 @@ describe("<LabsUserSettingsTab />", () => {
|
||||
// non-beta labs section
|
||||
expect(screen.getByText("Early previews")).toBeInTheDocument();
|
||||
const labsSections = container.getElementsByClassName("mx_SettingsSubsection");
|
||||
expect(labsSections).toHaveLength(10);
|
||||
expect(labsSections).toHaveLength(9);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,107 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { mocked } from "jest-mock";
|
||||
import {
|
||||
ClientEvent,
|
||||
EventType,
|
||||
type MatrixClient,
|
||||
type MatrixEvent,
|
||||
MatrixEventEvent,
|
||||
SyncState,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import AutoRageshakeStore from "../../../src/stores/AutoRageshakeStore";
|
||||
import { mkEvent, stubClient } from "../../test-utils";
|
||||
|
||||
jest.mock("../../../src/rageshake/submit-rageshake");
|
||||
jest.mock("../../../src/stores/WidgetStore");
|
||||
jest.mock("../../../src/stores/widgets/WidgetLayoutStore");
|
||||
|
||||
const TEST_SENDER = "@sender@example.com";
|
||||
|
||||
describe("AutoRageshakeStore", () => {
|
||||
const roomId = "!room:example.com";
|
||||
let client: MatrixClient;
|
||||
let utdEvent: MatrixEvent;
|
||||
let autoRageshakeStore: AutoRageshakeStore;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(true);
|
||||
|
||||
client = stubClient();
|
||||
|
||||
// @ts-ignore bypass private ctor for tests
|
||||
autoRageshakeStore = new AutoRageshakeStore();
|
||||
autoRageshakeStore.start();
|
||||
|
||||
utdEvent = mkEvent({
|
||||
event: true,
|
||||
content: {},
|
||||
room: roomId,
|
||||
user: TEST_SENDER,
|
||||
type: EventType.RoomMessage,
|
||||
});
|
||||
jest.spyOn(utdEvent, "isDecryptionFailure").mockReturnValue(true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("when the initial sync completed", () => {
|
||||
beforeEach(() => {
|
||||
client.emit(ClientEvent.Sync, SyncState.Syncing, SyncState.Stopped, { nextSyncToken: "abc123" });
|
||||
});
|
||||
|
||||
describe("and an undecryptable event occurs", () => {
|
||||
beforeEach(() => {
|
||||
client.emit(MatrixEventEvent.Decrypted, utdEvent);
|
||||
// simulate event grace period
|
||||
jest.advanceTimersByTime(5500);
|
||||
});
|
||||
|
||||
it("should send a to-device message", () => {
|
||||
expect(mocked(client).sendToDevice.mock.calls).toEqual([
|
||||
[
|
||||
"im.vector.auto_rs_request",
|
||||
new Map([
|
||||
[
|
||||
TEST_SENDER,
|
||||
new Map([
|
||||
[
|
||||
undefined,
|
||||
{
|
||||
"device_id": undefined,
|
||||
"event_id": utdEvent.getId(),
|
||||
"org.matrix.msgid": expect.any(String),
|
||||
"recipient_rageshake": undefined,
|
||||
"room_id": "!room:example.com",
|
||||
"sender_key": undefined,
|
||||
"session_id": undefined,
|
||||
"user_id": TEST_SENDER,
|
||||
},
|
||||
],
|
||||
]),
|
||||
],
|
||||
]),
|
||||
],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -407,11 +407,8 @@ If you run your own rageshake server to collect bug reports, the following optio
|
||||
not present in the config, the app will disable all rageshake functionality. Set to `https://rageshakes.element.io/api/submit` to submit
|
||||
rageshakes to us, or use your own rageshake server.
|
||||
You may also set the value to `"local"` if you wish to only store logs locally, in order to download them for debugging.
|
||||
2. `uisi_autorageshake_app`: If a user has enabled the "automatically send debug logs on decryption errors" flag, this option will be sent
|
||||
alongside the rageshake so the rageshake server can filter them by app name. By default, this will be `element-auto-uisi`
|
||||
(in contrast to other rageshakes submitted by the app, which use `element-web`).
|
||||
3. `existing_issues_url`: URL for where to find existing issues.
|
||||
4. `new_issue_url`: URL for where to submit new issues.
|
||||
2. `existing_issues_url`: URL for where to find existing issues.
|
||||
3. `new_issue_url`: URL for where to submit new issues.
|
||||
|
||||
If you would like to use [Sentry](https://sentry.io/) for rageshake data, add a `sentry` object to your config with the following values:
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user