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
13 changes: 8 additions & 5 deletions src/app/crypto/engineCrypto/EngineCrypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import { PerSessionBackupDownloader } from './perSessionBackupDownload';
import type { CryptoEventHandlerMap } from 'matrix-js-sdk/lib/crypto-api/CryptoEventHandlerMap';
import { createDebugLogger } from '$utils/debugLogger';
import { traceVerification, warnVerification } from '$utils/verificationTrace';
import { EngineVerificationRequest } from '../verification/request';
import {
EnginePhase,
Expand Down Expand Up @@ -132,7 +133,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 136 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:136:6: Use `Array#toSorted()` instead of `Array#sort()`.
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(',')}}`;
};

Expand Down Expand Up @@ -738,7 +739,7 @@
}
const request = new EngineVerificationRequest(this.#engineCall, state);
this.#verificationRequests.set(transactionId, request);
engineCryptoLog.info('general', 'Surfacing an incoming verification request', {
traceVerification('Surfacing an incoming verification request', {
sender,
transactionId,
isSelfVerification: state.isSelfVerification,
Expand All @@ -759,7 +760,7 @@
await this.#receiveSyncChanges({ toDeviceEvents: [event] });
if (!(await this.onIncomingKeyVerificationRequest(sender, transactionId))) {
const sentAt = (event.content as { timestamp?: number } | undefined)?.timestamp;
engineCryptoLog.warn('general', 'The engine kept ignoring a verification request', {
warnVerification('The engine kept ignoring a verification request', {
sender,
transactionId,
clockSkewSeconds:
Expand Down Expand Up @@ -835,7 +836,7 @@
if (typeof message.type === 'string' && message.type.startsWith('m.key.verification.')) {
const transactionId = (message.content as { transaction_id?: string })?.transaction_id;
if (transactionId && message.sender) {
engineCryptoLog.info('general', 'Received a verification to-device event', {
traceVerification('Received a verification to-device event', {
type: message.type,
sender: message.sender,
transactionId,
Expand Down Expand Up @@ -943,7 +944,9 @@
userId: this.#identity.userId,
})) ?? []) as EngineVerificationState[];
} catch (error) {
engineCryptoLog.warn('general', 'Could not list pending verification requests', error);
warnVerification('Could not list pending verification requests', {
reason: error instanceof Error ? error.message : String(error),
});
return;
}

Expand All @@ -953,7 +956,7 @@

const request = new EngineVerificationRequest(this.#engineCall, state);
this.#verificationRequests.set(state.flowId, request);
engineCryptoLog.warn('general', 'Recovered a verification request the sync path missed', {
warnVerification('Recovered a verification request the sync path missed', {
flowId: state.flowId,
otherUserId: state.otherUserId,
phase: state.phase,
Expand Down
40 changes: 40 additions & 0 deletions src/app/utils/verificationTrace.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as Sentry from '@sentry/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { traceVerification, warnVerification } from './verificationTrace';

vi.mock('@sentry/react', () => ({
addBreadcrumb: vi.fn<(input: unknown) => void>(),
logger: {
info: vi.fn<(message: string, attrs: unknown) => void>(),
warn: vi.fn<(message: string, attrs: unknown) => void>(),
},
}));

describe('verification tracing', () => {
beforeEach(() => vi.clearAllMocks());

it('reaches Sentry without depending on the opt-in debug logger', () => {
traceVerification('Received a verification to-device event', {
sender: '@me:e.org',
transactionId: '$f',
});

expect(Sentry.logger.info).toHaveBeenCalledWith(
'[crypto:verification] Received a verification to-device event',
{ sender: '@me:e.org', transactionId: '$f' }
);
expect(Sentry.addBreadcrumb).toHaveBeenCalledOnce();
});

it('drops attributes Sentry cannot index instead of failing', () => {
warnVerification('The engine kept ignoring a verification request', {
sender: '@me:e.org',
clockSkewSeconds: null,
});

expect(Sentry.logger.warn).toHaveBeenCalledWith(
'[crypto:verification] The engine kept ignoring a verification request',
{ sender: '@me:e.org' }
);
});
});
33 changes: 33 additions & 0 deletions src/app/utils/verificationTrace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as Sentry from '@sentry/react';

type TraceData = Record<string, string | number | boolean | null | undefined>;

const attributes = (data: TraceData): Record<string, string | number | boolean> => {
const out: Record<string, string | number | boolean> = {};
Object.entries(data).forEach(([key, value]) => {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
out[key] = value;
}
});
return out;
};

export const traceVerification = (message: string, data: TraceData = {}): void => {
Sentry.addBreadcrumb({
category: 'crypto.verification',
message,
level: 'info',
data: attributes(data),
});
Sentry.logger.info(`[crypto:verification] ${message}`, attributes(data));
};

export const warnVerification = (message: string, data: TraceData = {}): void => {
Sentry.addBreadcrumb({
category: 'crypto.verification',
message,
level: 'warning',
data: attributes(data),
});
Sentry.logger.warn(`[crypto:verification] ${message}`, attributes(data));
};
Loading