safeguards for monero subaddress list (#3242)
What changed, and why it matters
This commit adds safety checks to prevent the Cake Wallet app from crashing when its Monero subaddress list is unexpectedly empty. It does not appear to fix a security vulnerability that an attacker could exploit; rather, it is a robustness improvement against a rare, observed production crash.
Treat as a stability/robustness fix. No urgent security action required. If the empty-list condition is reproducible, investigate the underlying Monero wallet refresh/addSubaddress behavior to ensure subaddresses are not silently lost.
Security signals we found
Defensive null-safety / empty-list handling
Race-condition mitigation by snapshotting list before clear+add
No input validation, cryptographic, or authorization changes observed
No mention of security impact, CVE, researcher, or exploit in commit
Evidence from the diff
The patch makes three defensive changes in the Monero subaddress handling code: (1) it captures the refreshed subaddress list into a local variable before clearing and repopulating the observable list, reducing the window for race conditions; (2) it uses firstOrNull instead of first when reading the primary address, avoiding a StateError if the list is empty; and (3) after adding a new subaddress, it checks whether the list is empty before accessing .first.address, returning early instead of crashing. The commit message and inline comment explicitly describe these as safeguards against a crash that occurred once in production.
Changed components
cw_monero/lib/monero_subaddress_list.dartlib/view_model/wallet_address_list/wallet_address_list_view_model.dartlib/view_model/wallet_address_list/wallet_address_util.dartInspect captured patch +12 / −4
diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart
index 0c5284d..fc5181d 100644
--- a/cw_monero/lib/monero_subaddress_list.dart
+++ b/cw_monero/lib/monero_subaddress_list.dart
@@ -34,8 +34,9 @@ abstract class MoneroSubaddressListBase with Store {
try {
_isUpdating = true;
refresh(accountIndex: accountIndex);
+ final newAddrs = await getAll();
subaddresses.clear();
- subaddresses.addAll(await getAll());
+ subaddresses.addAll(newAddrs);
_isUpdating = false;
} catch (e) {
_isUpdating = false;
diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
index a2d1a3f..85293fd 100644
--- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
@@ -214,7 +214,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
final addressList = ObservableList<ListItem>();
if (wallet.type == WalletType.monero) {
- final primaryAddress = monero!.getSubaddressList(wallet).subaddresses.first;
+ final primaryAddress = monero!.getSubaddressList(wallet).subaddresses.firstOrNull;
final addressItems = monero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
final isPrimary = subaddress == primaryAddress;
diff --git a/lib/view_model/wallet_address_list/wallet_address_util.dart b/lib/view_model/wallet_address_list/wallet_address_util.dart
index d4df524..21bb143 100644
--- a/lib/view_model/wallet_address_list/wallet_address_util.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_util.dart
@@ -25,9 +25,16 @@ Future<void> createNewAddress(WalletBase wallet, String label) async {
await monero!
.getSubaddressList(wallet)
.addSubaddress(wallet, accountIndex: monero!.getCurrentAccount(wallet).id, label: label);
- final addr = await monero!
+ final subaddressList = await monero!
.getSubaddressList(wallet)
- .subaddresses
+ .subaddresses;
+ if(subaddressList.isEmpty) {
+ // this shouldn't happen, we just added an addr.
+ // somehow, it happened once in prod regardless.
+ // we just return instead of crashing, user can press the button again ig
+ return;
+ }
+ final addr = subaddressList
.first
.address; // first because the order is reversed
wallet.walletAddresses.manualAddresses.add(addr);
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.