Skip to content

fix(walletkit): upgrade WalletKit so a dApp can't rewrite its own session - #1064

Draft
CassioMG wants to merge 1 commit into
cg-dapp-sheet-error-boundariesfrom
cg-walletkit-upgrade
Draft

CassioMG wants to merge 1 commit into
cg-dapp-sheet-error-boundariesfrom
cg-walletkit-upgrade

Conversation

@CassioMG

@CassioMG CassioMG commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

Stacked on #1063. Upgrades @reown/walletkit from 1.4.1 to 1.5.6, with the @walletconnect/core, types and utils 2.23.10 packages it pins (from 2.23.1; the sign client WalletKit brought was 2.23.0). Nothing native changes.

The point is session updates. In WalletConnect, only the side that controls a session may change what the session covers, and in our sessions that's the wallet. The sign client we ship applied a session update from any connected dApp: it ran the update's validation without waiting for it and never checked who sent it. So a dApp could rewrite its own session, for example by dropping the Stellar namespace. From 2.23.10, the sign client waits for the validation and refuses any update the session's controller didn't send, before it changes anything.

Why 1.5.6 and not 1.6.0, the newest 1.x:

  • 1.6.0's sign client (2.25.0) has the same fix.
  • But from 2.24 it also sends the hash of every signed Stellar transaction to WalletConnect's relay, in the clear. 2.23.10 doesn't.
  • @walletconnect/react-native-compat stays at 2.23.1. Newer versions add WalletConnect Pay's native library, which WalletKit would then start at every launch, and which Android release builds' R8 rules don't cover.

No session_update listener is added. With this version the event can't fire for an update a dApp sends, and WalletKit doesn't forward it anyway.

Lines changed

Kind Files Lines added Lines removed
Source code 0 0 0
Tests 1 75 0
Other (package.json and yarn.lock) 2 81 150
Total 3 156 150

Only this PR's own changes, not those of the PRs it's stacked on.

Device check (iPhone SE simulator on iOS 27.2, Pixel 10 Pro emulator)

A local WalletConnect test client connects, sends a raw wc_sessionUpdate whose namespaces drop stellar, then two stellar_signMessage requests. The first is approved and the second cancelled. What the test client logged, the same on both devices:

Sending a raw wc_sessionUpdate without the stellar namespace
Wallet answered the session update: {"message":"Unauthorized update request.","code":3003}
Sending a short stellar_signMessage
Wallet answered: {"signature":"…"}
Sending a short stellar_signMessage
Wallet returned an error: User rejected the request
The request that follows the refused update opens as usual iOS Android
Implementation details (for agents)

What changed:

  • package.json, yarn.lock: @reown/walletkit 1.5.6; @walletconnect/core, @walletconnect/types and @walletconnect/utils 2.23.10. There's now a single copy of each @walletconnect package (before, 2.23.0 and 2.23.1 were both installed).
    • WalletKit 1.5 also brings @walletconnect/pay (JS). With compat at 2.23.1 its native module isn't there, so WalletConnectPay.isAvailable() is false and WalletKit doesn't start it. ios/Podfile.lock is unchanged.
  • __tests__/helpers/walletConnectSessionUpdate.test.ts (new): drives the sign client's own handler for an incoming wc_sessionUpdate from the dApp. It expects the update refused (error 3003, UNAUTHORIZED_UPDATE_REQUEST), the session left as approved, and no session_update event. It fails on 2.23.x before 2.23.10, where the update was applied.

The sign client's handler, before and after:

  • 2.23.0: this.isValidUpdate(...) without await, then session.update, sendResult, and session_update.
  • 2.23.10: await this.isValidUpdate(...), then if (session.peer.publicKey !== session.controller) throw getSdkError("UNAUTHORIZED_UPDATE_REQUEST").

Verification:

  • yarn test: 356 suites pass, 5,317 tests pass and 6 are skipped. That includes the new session-update test, which fails on 2.23.0 (the update was applied) and passes on 2.23.10.
  • yarn lint:ts passes, and ESLint and Prettier pass on the new test.
  • ios/Podfile.lock is unchanged. Both dev apps were rebuilt from this branch and start.
  • Dev builds on an iPhone SE (3rd generation) iOS 27.2 simulator and a Pixel 10 Pro emulator, with a local WalletConnect test client (log and screenshots above):
    • connecting works;
    • a dApp-sent session update is refused with error 3003, and the session keeps working;
    • sign-message approve and cancel work;
    • a stellar_signXDR payment is approved through Blockaid's notice and returns the signed XDR;
    • the dApp disconnects.
  • An earlier pass of this branch on WalletKit 1.6.0 behaved the same on both devices, before the switch to 1.5.6.
  • Since fix(walletkit): don't show a dApp request again after the app closed on it #1062's start check waits for its rejections: in this sign client a rejection doesn't wait for the network send, and an already-delivered request id isn't delivered twice.
  • I didn't run the repo's WalletConnect Maestro flows. They import the e2e wallet from secrets, and CI runs them only on PRs to main, release and v*.*.*. Running the iOS and Android E2E workflows on this branch before merge would cover them.

Follow-ups / out of scope:

  • A session that a dApp changed before this upgrade keeps those changes, for example extra chains or accounts. fix(walletkit): harden handling of dApp metadata, sessions and pairing links #1060 already disconnects sessions whose accounts aren't a list of strings. Checking stored sessions against what the wallet approved would close the rest.
  • The JS bundle grows by about 0.4 MB with @walletconnect/pay, which every WalletKit from 1.5 brings.

🤖 Generated with Claude Code

…sion

Upgrades @reown/walletkit from 1.4.1 to 1.5.6, with the @walletconnect/*
2.23.10 packages it pins (from 2.23.1, and sign-client 2.23.0).

The sign client we shipped applied a session update from any connected
dApp: it ran the update's validation without waiting for it and never
checked who sent it, so a dApp could change what its session covers,
for example drop the Stellar namespace. From 2.23.10 the sign client
waits for the validation and refuses an update not sent by the session's
controller, which in our sessions is the wallet. A test drives the sign
client's own handler to pin that.

1.5.6 rather than the newer 1.6.0: its 2.25.0 sign client also sends the
hash of every signed Stellar transaction to the relay in the clear,
which 2.23.10 doesn't. @walletconnect/react-native-compat stays at
2.23.1, so WalletConnect Pay's native module isn't added and WalletKit
doesn't start Pay; nothing native changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatednpm/​@​walletconnect/​types@​2.23.1 ⏵ 2.23.10100100729680
Updatednpm/​@​walletconnect/​utils@​2.23.1 ⏵ 2.23.1098 +1100729680
Updatednpm/​@​walletconnect/​core@​2.23.1 ⏵ 2.23.1098 +1100749680
Updatednpm/​@​reown/​walletkit@​1.4.1 ⏵ 1.5.6100100100 +195 +780

View full report

@github-actions

Copy link
Copy Markdown
Contributor

Android Emulator preview APK is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-d87463b51521e482327f
Backend: sandbox (CassioMG). SDF collaborators only — install instructions in the release description.

@github-actions

Copy link
Copy Markdown
Contributor

iOS Simulator preview build is ready: https://github.com/stellar/freighter-mobile/releases/tag/untagged-35965b8b7a72dea9b2a5
Backend: sandbox (CassioMG). SDF collaborators only — install instructions in the release description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant