From 361b0aeebe6fa18efa9e6f0ecdca9030d97200fe Mon Sep 17 00:00:00 2001 From: Mariano Pardo <marian2js@users.noreply.github.com> Date: Fri, 24 Feb 2023 19:47:23 +0100 Subject: [PATCH 01/12] chore: remove unnecessarily disabled eslint rule --- src/ApiClient.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ApiClient.ts b/src/ApiClient.ts index 940f178ef..a7e77d12a 100644 --- a/src/ApiClient.ts +++ b/src/ApiClient.ts @@ -91,7 +91,6 @@ const isAbortError = (err?: Error): boolean => { return false } -// eslint-disable-next-line @typescript-eslint/no-explicit-any const isAuthError = (err?: GrpcError): boolean => { if (err && err.code === ERR_CODE_UNAUTHENTICATED) { return true @@ -99,7 +98,6 @@ const isAuthError = (err?: GrpcError): boolean => { return false } -// eslint-disable-next-line @typescript-eslint/no-explicit-any const isNotAuthError = (err?: GrpcError): boolean => !isAuthError(err) /** From 0102061e2cc0eb989d0c23f66aadf746a72175a3 Mon Sep 17 00:00:00 2001 From: Brian Covey <dvorak@mac.com> Date: Wed, 25 Oct 2023 12:06:55 -0700 Subject: [PATCH 02/12] Update LICENSE Updated copyright notice --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index ff0f50665..11eedfd19 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 XMTP Labs (xmtp.com) +Copyright (c) 2023 XMTP (xmtp.org) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2502d9e041025029eb4f82e881637a5130d6bbe2 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Tue, 28 Nov 2023 14:42:28 -0600 Subject: [PATCH 03/12] fix: do not log error on stream disconnect --- src/ApiClient.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ApiClient.ts b/src/ApiClient.ts index 1859f6124..acd29a158 100644 --- a/src/ApiClient.ts +++ b/src/ApiClient.ts @@ -290,10 +290,7 @@ export default class HttpApiClient implements ApiClient { if (isAbortError(err) || abortController.signal.aborted) { return } - console.info( - 'Stream connection closed. Resubscribing', - err.toString() - ) + console.info('Stream connection closed. Resubscribing') if (new Date().getTime() - startTime < 1000) { await sleep(1000) From 9c2552c8157b7700cfb1d95c7af56d33d162e23a Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Tue, 28 Nov 2023 15:12:02 -0600 Subject: [PATCH 04/12] fix: filter out conversations with invalid topics --- src/conversations/Conversations.ts | 35 ++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/conversations/Conversations.ts b/src/conversations/Conversations.ts index efabd1e86..df7fa205a 100644 --- a/src/conversations/Conversations.ts +++ b/src/conversations/Conversations.ts @@ -25,6 +25,21 @@ const messageHasHeaders = (msg: MessageV1): boolean => { return Boolean(msg.recipientAddress && msg.senderAddress) } +// validate that a topic only contains ASCII characters +const isValidTopic = (topic: string): boolean => { + // eslint-disable-next-line no-control-regex + const regex = /^[\x00-\x7F]+$/ + const index = topic.indexOf('0/') + if (index !== -1) { + const unwrappedTopic = topic.substring( + index + 2, + topic.lastIndexOf('/proto') + ) + return regex.test(unwrappedTopic) + } + return false +} + /** * Conversations allows you to view ongoing 1:1 messaging sessions with another wallet */ @@ -81,14 +96,14 @@ export default class Conversations<ContentTypes = any> { }) await this.client.keystore.saveV1Conversations({ - conversations: Array.from(seenPeers).map( - ([peerAddress, createdAt]) => ({ + conversations: Array.from(seenPeers) + .map(([peerAddress, createdAt]) => ({ peerAddress, createdNs: dateToNs(createdAt), topic: buildDirectMessageTopic(peerAddress, this.client.address), context: undefined, - }) - ), + })) + .filter((c) => isValidTopic(c.topic)), }) return ( @@ -158,11 +173,13 @@ export default class Conversations<ContentTypes = any> { shouldThrow = false ): Promise<ConversationV2<ContentTypes>[]> { const { responses } = await this.client.keystore.saveInvites({ - requests: envelopes.map((env) => ({ - payload: env.message as Uint8Array, - timestampNs: Long.fromString(env.timestampNs as string), - contentTopic: env.contentTopic as string, - })), + requests: envelopes + .map((env) => ({ + payload: env.message as Uint8Array, + timestampNs: Long.fromString(env.timestampNs as string), + contentTopic: env.contentTopic as string, + })) + .filter((req) => isValidTopic(req.contentTopic)), }) const out: ConversationV2<ContentTypes>[] = [] From 9e6bbffc06daf2b0ebb5ad14429f23552d354ae3 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Tue, 28 Nov 2023 20:10:50 -0600 Subject: [PATCH 05/12] refactor: move isValidTopic to utils/topic --- src/conversations/Conversations.ts | 16 +--------------- src/utils/topic.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/conversations/Conversations.ts b/src/conversations/Conversations.ts index df7fa205a..ab1f5a4d4 100644 --- a/src/conversations/Conversations.ts +++ b/src/conversations/Conversations.ts @@ -12,6 +12,7 @@ import { buildUserIntroTopic, buildUserInviteTopic, dateToNs, + isValidTopic, nsToDate, } from '../utils' import { PublicKeyBundle } from '../crypto' @@ -25,21 +26,6 @@ const messageHasHeaders = (msg: MessageV1): boolean => { return Boolean(msg.recipientAddress && msg.senderAddress) } -// validate that a topic only contains ASCII characters -const isValidTopic = (topic: string): boolean => { - // eslint-disable-next-line no-control-regex - const regex = /^[\x00-\x7F]+$/ - const index = topic.indexOf('0/') - if (index !== -1) { - const unwrappedTopic = topic.substring( - index + 2, - topic.lastIndexOf('/proto') - ) - return regex.test(unwrappedTopic) - } - return false -} - /** * Conversations allows you to view ongoing 1:1 messaging sessions with another wallet */ diff --git a/src/utils/topic.ts b/src/utils/topic.ts index 5290cbf51..6bff0ec57 100644 --- a/src/utils/topic.ts +++ b/src/utils/topic.ts @@ -35,3 +35,18 @@ export const buildUserPrivateStoreTopic = (addrPrefixedKey: string): string => { // e.g. "0x1111111111222222222233333333334444444444/key_bundle" return buildContentTopic(`privatestore-${addrPrefixedKey}`) } + +// validate that a topic only contains ASCII characters +export const isValidTopic = (topic: string): boolean => { + // eslint-disable-next-line no-control-regex + const regex = /^[\x00-\x7F]+$/ + const index = topic.indexOf('0/') + if (index !== -1) { + const unwrappedTopic = topic.substring( + index + 2, + topic.lastIndexOf('/proto') + ) + return regex.test(unwrappedTopic) + } + return false +} From d22918d3e21e25e1ed87f6717bbc8c80f3135b68 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Tue, 28 Nov 2023 20:30:08 -0600 Subject: [PATCH 06/12] refactor: narrow character range --- src/utils/topic.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/topic.ts b/src/utils/topic.ts index 6bff0ec57..7e4b25e63 100644 --- a/src/utils/topic.ts +++ b/src/utils/topic.ts @@ -36,10 +36,10 @@ export const buildUserPrivateStoreTopic = (addrPrefixedKey: string): string => { return buildContentTopic(`privatestore-${addrPrefixedKey}`) } -// validate that a topic only contains ASCII characters +// validate that a topic only contains ASCII characters 33-127 export const isValidTopic = (topic: string): boolean => { // eslint-disable-next-line no-control-regex - const regex = /^[\x00-\x7F]+$/ + const regex = /^[\x21-\x7F]+$/ const index = topic.indexOf('0/') if (index !== -1) { const unwrappedTopic = topic.substring( From bfbb7f30ae5d095051dd77c4b2a821b3675c2584 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Tue, 28 Nov 2023 20:30:39 -0600 Subject: [PATCH 07/12] test: add tests --- test/utils/topic.test.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/utils/topic.test.ts diff --git a/test/utils/topic.test.ts b/test/utils/topic.test.ts new file mode 100644 index 000000000..d28bc379b --- /dev/null +++ b/test/utils/topic.test.ts @@ -0,0 +1,26 @@ +import { buildContentTopic, isValidTopic } from '../../src/utils/topic' + +describe('topic utils', () => { + describe('isValidTopic', () => { + it('validates topics correctly', () => { + expect(isValidTopic(buildContentTopic('foo'))).toBe(true) + expect(isValidTopic(buildContentTopic('123'))).toBe(true) + expect(isValidTopic(buildContentTopic('bar987'))).toBe(true) + expect(isValidTopic(buildContentTopic('*&+-)'))).toBe(true) + expect(isValidTopic(buildContentTopic('%#@='))).toBe(true) + expect(isValidTopic(buildContentTopic('<;.">'))).toBe(true) + expect(isValidTopic(buildContentTopic(String.fromCharCode(33)))).toBe( + true + ) + expect(isValidTopic(buildContentTopic('∫ß'))).toBe(false) + expect(isValidTopic(buildContentTopic('\xA9'))).toBe(false) + expect(isValidTopic(buildContentTopic('\u2665'))).toBe(false) + expect(isValidTopic(buildContentTopic(String.fromCharCode(1)))).toBe( + false + ) + expect(isValidTopic(buildContentTopic(String.fromCharCode(23)))).toBe( + false + ) + }) + }) +}) From 01757fba14bb55ed00a9745fd9d46285b6641fe1 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Wed, 29 Nov 2023 14:12:21 -0600 Subject: [PATCH 08/12] test: add random topic testing --- test/utils/topic.test.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/utils/topic.test.ts b/test/utils/topic.test.ts index d28bc379b..1f0879754 100644 --- a/test/utils/topic.test.ts +++ b/test/utils/topic.test.ts @@ -1,4 +1,9 @@ -import { buildContentTopic, isValidTopic } from '../../src/utils/topic' +import { + buildContentTopic, + buildDirectMessageTopicV2, + isValidTopic, +} from '../../src/utils/topic' +import crypto from '../../src/crypto/crypto' describe('topic utils', () => { describe('isValidTopic', () => { @@ -22,5 +27,22 @@ describe('topic utils', () => { false ) }) + + it('validates random topics correctly', () => { + const topics = Array.from({ length: 100 }).map(() => + buildDirectMessageTopicV2( + Buffer.from(crypto.getRandomValues(new Uint8Array(32))) + .toString('base64') + .replace(/=*$/g, '') + // Replace slashes with dashes so that the topic is still easily split by / + // We do not treat this as needing to be valid Base64 anywhere + .replace('/', '-') + ) + ) + + topics.forEach((topic) => { + expect(isValidTopic(topic)).toBe(true) + }) + }) }) }) From 5308a4fbc9b8e8c1c42e4b03e82fad5f2bc48336 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Wed, 29 Nov 2023 15:37:28 -0600 Subject: [PATCH 09/12] docs: add note about BigInt polyfills --- README.md | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d2a0873a1..39b995c88 100644 --- a/README.md +++ b/README.md @@ -37,30 +37,13 @@ Additional configuration is required in React environments due to the removal of ## Troubleshoot -If you get into issues with Buffer and polyfills check out our [fix below](https://xmtp.org/docs/developer-quickstart#troubleshooting). +### Buffer polyfill -### Create React App +If you get into issues with Buffer and polyfills check out our [solutions](https://xmtp.org/docs/faq#why-my-app-is-failing-saying-buffer-is-not-found). -Use `react-scripts` prior to version `5.0.0`. For example: +### BigInt polyfill -```bash -npx create-react-app my-app --scripts-version 4.0.2 -``` - -Or downgrade after creating your app. - -### Next.js - -In `next.config.js`: - -```js -webpack: (config, { isServer }) => { - if (!isServer) { - config.resolve.fallback.fs = false - } - return config -} -``` +Some older browsers do not support `BigInt` (see [compatibility list](https://caniuse.com/bigint)). Unfortunately, the way this SDK uses `BigInt` is incompatible with polyfills. To ensure that a polyfill is not added to your application bundle, update your [browserslist](https://github.com/browserslist/browserslist) configuration to exclude browsers that don't support `BigInt`. ## Usage From a2b3861a019a9f427b20b36a16a5186f3ffd1cf6 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <rygine@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:16:48 -0600 Subject: [PATCH 10/12] Update README.md Co-authored-by: J-Ha <5481259+jhaaaa@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 39b995c88..7655d7dc8 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Additional configuration is required in React environments due to the removal of ### Buffer polyfill -If you get into issues with Buffer and polyfills check out our [solutions](https://xmtp.org/docs/faq#why-my-app-is-failing-saying-buffer-is-not-found). +If you run into issues with Buffer and polyfills, see this [solution](https://xmtp.org/docs/faq#why-my-app-is-failing-saying-buffer-is-not-found). ### BigInt polyfill From cc92dd9a08d581c174d742e2a8593f9cb3a6d1c8 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <rygine@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:17:10 -0600 Subject: [PATCH 11/12] Update README.md Co-authored-by: J-Ha <5481259+jhaaaa@users.noreply.github.com> --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7655d7dc8..708f08cc0 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,9 @@ If you run into issues with Buffer and polyfills, see this [solution](https://xm ### BigInt polyfill -Some older browsers do not support `BigInt` (see [compatibility list](https://caniuse.com/bigint)). Unfortunately, the way this SDK uses `BigInt` is incompatible with polyfills. To ensure that a polyfill is not added to your application bundle, update your [browserslist](https://github.com/browserslist/browserslist) configuration to exclude browsers that don't support `BigInt`. +This SDK uses `BigInt` in a way that's incompatible with polyfills. To ensure that a polyfill isn't added to your application bundle, update your [browserslist](https://github.com/browserslist/browserslist) configuration to exclude browsers that don't support `BigInt`. + +For the list of browsers that don't support `BigInt`, see this [compatibility list](https://caniuse.com/bigint). ## Usage From b4836dff46f0f2a4b6df69769459cf252bf3d32a Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer <ry@xmtp.com> Date: Thu, 30 Nov 2023 13:51:48 -0600 Subject: [PATCH 12/12] fix: ensure slashes aren't part of topics --- src/Invitation.ts | 2 +- test/utils/topic.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Invitation.ts b/src/Invitation.ts index b9286d2b7..07cecd8f5 100644 --- a/src/Invitation.ts +++ b/src/Invitation.ts @@ -47,7 +47,7 @@ export class InvitationV1 implements invitation.InvitationV1 { .replace(/=*$/g, '') // Replace slashes with dashes so that the topic is still easily split by / // We do not treat this as needing to be valid Base64 anywhere - .replace('/', '-') + .replace(/\//g, '-') ) const keyMaterial = crypto.getRandomValues(new Uint8Array(32)) diff --git a/test/utils/topic.test.ts b/test/utils/topic.test.ts index 1f0879754..2baf3d6e0 100644 --- a/test/utils/topic.test.ts +++ b/test/utils/topic.test.ts @@ -36,7 +36,7 @@ describe('topic utils', () => { .replace(/=*$/g, '') // Replace slashes with dashes so that the topic is still easily split by / // We do not treat this as needing to be valid Base64 anywhere - .replace('/', '-') + .replace(/\//g, '-') ) )