Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app/crypto/engineCrypto/EngineCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@

const entries = Object.entries(value as Record<string, unknown>)
.filter(([, item]) => item !== undefined)
.sort(([a], [b]) => (a < b ? -1 : 1));

Check warning on line 132 in src/app/crypto/engineCrypto/EngineCrypto.ts

View workflow job for this annotation

GitHub Actions / Lint

unicorn(no-array-sort)

src/app/crypto/engineCrypto/EngineCrypto.ts:132:6: Use `Array#toSorted()` instead of `Array#sort()`.
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(',')}}`;
};

Expand Down Expand Up @@ -366,6 +366,7 @@
this.#identity = identity;
this.#backupDownloader = new PerSessionBackupDownloader({
mx,
getBackupVersion: () => this.getActiveSessionBackupVersion().catch(() => null),
importSession: (roomId, session) => this.#importBackedUpSession(roomId, session),
now: () => Date.now(),
});
Expand Down
18 changes: 18 additions & 0 deletions src/app/crypto/engineCrypto/perSessionBackupDownload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ const rateLimited = (retryAfterMs: number) =>

describe('PerSessionBackupDownloader', () => {
let clock = 0;
let backupVersion: string | null = '7';

beforeEach(() => {
clock = 0;
backupVersion = '7';
});

const make = (
Expand All @@ -31,6 +33,7 @@ describe('PerSessionBackupDownloader', () => {
) => {
const downloader = new PerSessionBackupDownloader({
mx: { http: { authedRequest } } as unknown as MatrixClient,
getBackupVersion: async () => backupVersion,
importSession,
now: () => clock,
});
Expand All @@ -48,9 +51,24 @@ describe('PerSessionBackupDownloader', () => {

expect(authedRequest).toHaveBeenCalledTimes(1);
expect(authedRequest.mock.calls[0]?.[1]).toBe('/room_keys/keys/!r%3Ae.org/S1');
expect(authedRequest.mock.calls[0]?.[2]).toEqual({ version: '7' });
expect(importSession).toHaveBeenCalledTimes(1);
});

it('does not query the backup when no active version is known', async () => {
backupVersion = null;
const authedRequest = vi.fn<(...args: never[]) => Promise<unknown>>(async () => ({
session_data: {},
}));
const { downloader, importSession } = make(authedRequest);

downloader.request({ roomId: '!r:e.org', sessionId: 'S1' });
await settle();

expect(authedRequest).not.toHaveBeenCalled();
expect(importSession).not.toHaveBeenCalled();
});

it('does not hammer the backup for a session it is already fetching', async () => {
const authedRequest = vi.fn<(...args: never[]) => Promise<unknown>>(async () => ({
session_data: {},
Expand Down
9 changes: 8 additions & 1 deletion src/app/crypto/engineCrypto/perSessionBackupDownload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type SessionRef = { roomId: string; sessionId: string };

export type BackupDownloadHost = {
mx: MatrixClient;
getBackupVersion: () => Promise<string | null>;
importSession: (roomId: string, session: KeyBackupSession) => Promise<boolean>;
now: () => number;
};
Expand Down Expand Up @@ -91,11 +92,17 @@ export class PerSessionBackupDownloader {
$sessionId: ref.sessionId,
});

const version = await this.#host.getBackupVersion();
if (!version) {
this.#missingUntil.set(key, this.#host.now() + BACKOFF_TIME_MS);
return;
}

try {
const session = await this.#host.mx.http.authedRequest<KeyBackupSession>(
Method.Get,
path,
{},
{ version },
undefined,
{ prefix: ClientPrefix.V3 }
);
Expand Down
5 changes: 2 additions & 3 deletions src/app/crypto/pushDecrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,10 @@ export const decryptPushEventNatively = async (
sender: decrypted.sender ?? event.sender,
};
} catch (error) {
// Expected while the to-device key is still in flight, so not a warning.
pushDecryptLog.info(
pushDecryptLog.warn(
'notification',
'Native push decryption unavailable, falling back to the js-sdk path',
error
{ reason: error instanceof Error ? error.message : String(error) }
);
return null;
}
Expand Down
8 changes: 1 addition & 7 deletions src/app/pages/client/BackgroundNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -592,13 +592,7 @@ export function BackgroundNotifications() {
.catch((err) => {
if (disposed) return;
log.error('failed to start background client for', session.userId, err);
debugLog.error('notification', 'Failed to start background client', {
userId: session.userId,
error: err,
});
Sentry.captureException(err, {
tags: { component: 'BackgroundNotifications' },
});
debugLog.error('notification', 'Failed to start background client', err);

// Remove the stuck/failed client from current so future runs (or the
// retry below) can attempt a fresh start.
Expand Down
1 change: 1 addition & 0 deletions src/instrument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ if (dsn && sentryEnabled) {
beforeSendLog(log) {
// Drop debug-level logs in production to reduce noise and quota usage
if (log.level === 'debug' && environment === 'production') return null;
if (typeof log.message === 'string' && log.message.startsWith('[sable:')) return null;
// Redact Matrix IDs and tokens from the log message string
if (typeof log.message === 'string') {
log.message = scrubMatrixIds(log.message);
Expand Down
Loading