From 954942284cf876a41ec975a4aa15b1681131869b Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 8 Jul 2026 19:44:06 +0000 Subject: [PATCH] fix(sdk-core): throw NeedUserSignupError when sharing key pubkey is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In shareWallet(), when needsKeychain is true and the recipient has never logged in (so /user/sharingkey returns no pubkey), the undefined pubkey was silently passed into prepareSharedKeychain() → encryptPrvForUser() → Buffer.from(undefined, 'hex'), causing a cryptic error that the UI surfaced as "wallet password incorrect". The bulk sharing path (createBulkWalletShare) already handled this by throwing NeedUserSignupError when pubkey is absent. Apply the same guard to the single-wallet shareWallet() path so callers receive a clear, actionable error indicating the recipient must complete account setup before keys can be shared. Also add two unit tests: - verifies NeedUserSignupError is thrown when pubkey is missing and spend permission is requested - verifies view-only shares (no spend permission) succeed even when pubkey is absent (no keychain needed for view shares) Ticket: POL-41 Session-Id: 963e6992-8423-4bd9-a7b7-06c6352ab017 Task-Id: 80a26fb6-aef8-4b14-868b-d2cb5259805a --- modules/bitgo/test/v2/unit/wallet.ts | 43 +++++++++++++++++++++ modules/sdk-core/src/bitgo/wallet/wallet.ts | 3 ++ 2 files changed, 46 insertions(+) diff --git a/modules/bitgo/test/v2/unit/wallet.ts b/modules/bitgo/test/v2/unit/wallet.ts index 761fb25056..efccead059 100644 --- a/modules/bitgo/test/v2/unit/wallet.ts +++ b/modules/bitgo/test/v2/unit/wallet.ts @@ -21,6 +21,7 @@ import { ManageUnspentsOptions, MessageStandardType, MessageTypes, + NeedUserSignupError, PopulatedIntent, PrebuildTransactionOptions, PrebuildTransactionWithIntentOptions, @@ -1994,6 +1995,48 @@ describe('V2 Wallet:', function () { createShareNock.isDone().should.be.True(); }); + it('should throw NeedUserSignupError when recipient pubkey is missing and spend permission is requested', async function () { + const userId = '456'; + const email = 'newuser@sdktest.com'; + const permissions = 'view,spend'; + const walletPassphrase = 'bitgo1234'; + + // Simulate a user who has not yet logged in / set up their ECDH key + const getSharingKeyNock = nock(bgUrl) + .post('/api/v1/user/sharingkey', { email }) + .reply(200, { userId }); + + await wallet + .shareWallet({ email, permissions, walletPassphrase }) + .should.be.rejectedWith(NeedUserSignupError); + + getSharingKeyNock.isDone().should.be.True(); + }); + + it('should not throw NeedUserSignupError when recipient pubkey is missing but spend permission is not requested', async function () { + const userId = '456'; + const email = 'newuser@sdktest.com'; + const permissions = 'view'; + + // Sharing key without pubkey is fine for view-only shares + const getSharingKeyNock = nock(bgUrl) + .post('/api/v1/user/sharingkey', { email }) + .reply(200, { userId }); + + const createShareNock = nock(bgUrl) + .post(`/api/v2/tbtc/wallet/${wallet.id()}/share`, { + user: userId, + permissions, + skipKeychain: true, + }) + .reply(200, {}); + + await wallet.shareWallet({ email, permissions }); + + getSharingKeyNock.isDone().should.be.True(); + createShareNock.isDone().should.be.True(); + }); + describe('Hot Wallet Sharing', function () { const userId = '123'; const email = 'shareto@sdktest.com'; diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index a8c1952cf5..59bc1b39ab 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -2064,6 +2064,9 @@ export class Wallet implements IWallet { })) as any; let sharedKeychain; if (needsKeychain) { + if (!sharing.pubkey) { + throw new NeedUserSignupError(sharing.userId); + } sharedKeychain = await this.prepareSharedKeychain( params.walletPassphrase, sharing.pubkey,