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, '-')
         )
       )